53501_3_MartynovSA_lab3
.pdfтому же ресурсу, но означают разные условия. Операция ожидания атомарно освобождает мьютекс и блокирует процесс до момента уведомления о том, что условие выполнено[4].
В листинге 18 происходит инициализация условных переменных, но для организации полноценной работы их не достаточно. Поэтому в качестве вспомогательного объекта синхронизации используется критическая секция.
Листинг 18: Основной файл инициализации примитивов синхронизации (src/SynchronizationPrimitives/ConditionVariable/main.cpp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include < windows .h >
#include < string .h >
#include < stdio .h >
#include < conio .h >
#include < tchar .h >
#include " thread .h"
#include " utils .h"
#include " Logger .h"
//глобальные переменные:
struct FIFOQueue queue ; // ст |
р |
уктур |
а очере |
ди |
|
struct Configuration config ; // кон |
фигурация программы |
||||
bool isDone = false ; |
// Признак зав |
ершения |
|
||
HANDLE * allhandlers ; |
// массив |
всех |
создаваемых потоков |
// критическая секция общая и для писателей и для читателей
CRITICAL_SECTION crs ;
// условная переменная для потоков -писателей
CONDITION_VARIABLE condread ;
// условная переменная для потоков -читателей
CONDITION_VARIABLE condwrite ;
int _tmain ( int argc , _TCHAR * argv []) {
Logger log ( _T (" ConditionVariable "));
if ( argc < 2)
// Используем конфигурацию по -умолчанию
SetDefaultConfig (& config , & log );
else
// Загрузка конфига из файла
SetConfig ( argv [1] , & config , & log );
// создаем необходимые потоки без их запуска
CreateAllThreads (& config , & log );
41
37// Инициализируем очередь
38queue . full = 0;
39queue . readindex = 0;
40queue . writeindex = 0;
41queue . size = config . sizeOfQueue ;
42 queue . data = new _TCHAR *[ config . sizeOfQueue ];
43// инициализируем средство синхронизации
44InitializeCriticalSection (& crs );
45InitializeConditionVariable (& condread );
46InitializeConditionVariable (& condwrite );
47 |
|
48 |
// запускаем потоки на исполнение |
49 |
for ( int i = 0; i < config . numOfReaders + config . numOfWriters + 1; i ++) |
50 |
ResumeThread ( allhandlers [i ]) ; |
51 |
|
52// ожидаем завершения всех потоков
53WaitForMultipleObjects ( config . numOfReaders + config . numOfWriters + 1,
54allhandlers , TRUE , 5000) ;
55 |
|
56 |
// закрываем handle потоков |
57 |
for ( int i = 0; i < config . numOfReaders + config . numOfWriters + 1; i ++) |
58CloseHandle ( allhandlers [i ]) ;
59// удаляем объект синхронизации
60DeleteCriticalSection (& crs );
61 |
|
62 |
// Очистка памяти |
63 |
for ( size_t i = 0; i != config . sizeOfQueue ; ++ i) |
64if ( queue . data [i ])
65free ( queue . data [i ]) ; // _wcsdup использует calloc
66delete [] queue . data ;
67
68// Завершение работы
69log . loudlog ( _T (" All tasks are done !"));
70_getch () ;
71return 0;
72}
Читатели (листинг 19) и писатели (листинг 20) входят в критическую секцию, но для работы ожидают условную переменную.
Листинг 19: Потоки читатели, синхронизация через условные переменные и критические секции (src/SynchronizationPrimitives/ConditionVariable/threadReader.cpp)
1 # include < windows .h >
2 # include < stdio .h >
3
42
4 |
# include " utils .h" |
|
5 |
|
|
6 |
DWORD |
WINAPI ThreadReaderHandler ( LPVOID prm ) { |
7 |
int |
myid = ( int ) prm ; |
8 |
|
|
9Logger log ( _T (" ConditionVariable . ThreadReader ") , myid );
10extern bool isDone ;
11extern struct FIFOQueue queue ;
12extern struct Configuration config ;
13extern CRITICAL_SECTION crs ;
14extern CONDITION_VARIABLE condread ;
15extern CONDITION_VARIABLE condwrite ;
16
17while ( isDone != true ) {
18// Захват объекта синхронизации
19log . quietlog ( _T (" Waining for Critical Section "));
20EnterCriticalSection (& crs );
21log . quietlog ( _T (" Get Critical Section "));
22 |
|
|
23 |
log . quietlog ( _T (" Waining for empty space in the |
queue ")); |
24 |
while (!( queue . readindex != queue . writeindex || |
queue . full == 1) ) |
25 |
// спим пока в очереди не появятся данные |
|
26SleepConditionVariableCS (& condread , &crs , INFINITE );
27log . quietlog ( _T (" Get space in the queue "));
28
29// взяли данные , значит очередь не пуста
30queue . full = 0;
31// печатаем принятые данные
32 |
log . loudlog ( _T (" Reader %d get data : \"% s \" from position %d") , myid , |
|
33 |
queue . data [ queue . readindex ], queue . readindex ); |
|
34 |
free ( queue . data [ queue . readindex ]) ; |
// очищаем очередь от данных |
35 |
queue . data [ queue . readindex ] = NULL ; |
|
36 |
queue . readindex = ( queue . readindex |
+ 1) % queue . size ; |
37 |
|
|
38// шлем сигнал потокам -читателям
39log . quietlog ( _T (" Wake Condition Variable "));
40WakeConditionVariable (& condwrite );
41// освобождение синхронизируемого объекта
42log . quietlog ( _T (" Leave Critical Section "));
43LeaveCriticalSection (& crs );
44
45// задержка
46Sleep ( config . readersDelay );
47}
48log . loudlog ( _T (" Reader %d finishing work ") , myid );
43
49 return 0;
50 }
Листинг 20: Потоки писатели, синхронизация через условные переменные и критические
секции (src/SynchronizationPrimitives/ConditionVariable/threadWriter.cpp)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include < windows .h >
#include < stdio .h >
#include < tchar .h >
#include " utils .h"
DWORD WINAPI ThreadWriterHandler ( LPVOID prm ) {
int myid = ( int ) prm ;
Logger log ( _T (" ConditionVariable . ThreadWriter ") , myid );
extern |
bool isDone ; |
|
|
|
extern struct FIFOQueue queue ; |
|
|
|
|
extern struct Configuration config ; |
|
|||
extern CRITICAL_SECTION crs ; |
|
|
|
|
extern CONDITION_VARIABLE condread ; |
|
|||
extern |
CONDITION_VARIABLE condwrite ; |
|
||
_TCHAR |
tmp [50]; |
|
|
|
int msgnum = 0; // номер передаваемого сообщения |
|
|||
while ( isDone != true ) { |
|
|
|
|
// Захват синхронизирующего объекта |
|
|||
log . quietlog ( _T (" Waining for |
Critical Section ")); |
|||
EnterCriticalSection (& crs ); |
|
|
|
|
log . quietlog ( _T (" Get Critical Section ")); |
|
|||
log . quietlog ( _T (" Waining for empty space in the queue ")); |
||||
while |
(!( queue . readindex != queue . writeindex |
|| ! queue . full == 1) ) |
||
// спим пока в очереди не освободится место |
|
|||
SleepConditionVariableCS (& condwrite , &crs , |
INFINITE ); |
|||
log . quietlog ( _T (" Get space in the queue ")); |
|
|||
// заносим в очередь данные |
|
|
|
|
swprintf_s (tmp , _T (" writer_id |
= |
%d numMsg = %3 d") , myid , msgnum ); |
||
queue . data [ queue . writeindex ] |
= |
_wcsdup ( tmp ); |
|
|
msgnum ++; |
|
|
|
// печатаем принятые данные
log . loudlog ( _T (" Writer %d put data : \"% s \" in position %d") , myid ,
queue . data [ queue . writeindex ], queue . writeindex );
44
40 |
queue . writeindex = ( queue . writeindex + 1) % queue . size ; |
41 |
// если очередь заполнилась |
42 |
queue . full = queue . writeindex == queue . readindex ? 1 : 0; |
43 |
|
44if ( queue . full == 1)
45log . loudlog ( _T (" Queue is full "));
46// шлем сигнал потокам -читателям
47log . quietlog ( _T (" Wake Condition Variable "));
48WakeConditionVariable (& condread );
49// освобождение синхронизируемого объекта
50log . quietlog ( _T (" Leave Critical Section "));
51LeaveCriticalSection (& crs );
52
53// задержка
54Sleep ( config . writersDelay );
55}
56log . loudlog ( _T (" Writer %d finishing work ") , myid );
57return 0;
58}
45
Результат работы сразу с двумя примитивами показан на рисунке 6. Можно заметить, что читатели не по порядку обрабатывают сообщения, но все сообщения оказываются обработанными.
Рис. 6: Условные переменные и критические секции.
46
1.6Задача читатели-писатели (для потоков одного процесса)
Рассмотрим частный случай этой задачи для демонстрации использования объектовсобытий для синхронизации доступа к памяти.
Задание: необходимо решить задачу одного писателя и N читателей. Для синхронизации разрешено использовать только объекты-события, в качестве разделяемого ресурса – разделяемую память (share memory). Писатель пишет в share memory сообщение и ждёт, пока все читатели не прочитают данное сообщение.
Задача должна быть решена сначала для потоков, принадлежащих одному процессу, а затем – разным независимым процессам.
Для этой задачи были внесены некоторые изменения в файл с сервисными функциями, которые позволили запустить только один поток-писатель. Примитивы синхронизации (события) и общая память инициализируются в листинге 21, а используются писателем и читателями в листинге 22 и 23[1].
Листинг 21: Основной файл (src/SynchronizationPrimitives/ThreadsReaderWriter/main.cpp)
1 # include < windows .h >
2 # include < string .h >
3 # include < stdio .h >
4 # include < conio .h >
5 # include < tchar .h >
6
7 # include " thread .h"
8 # include " utils .h"
9 # include " Logger .h"
10
11// глобальные переменные:
12struct Configuration config ; // конфигурация программы
13bool isDone = false ; // флаг завершения
14 |
HANDLE * allhandlers ; // массив всех создаваемых |
потоков |
|
|||||
15 |
|
|
|
|
|
|
|
|
16 // события для синхронизации: |
|
|
|
|
|
|||
17 |
HANDLE canReadEvent ; // писатель записал сообщение |
(ручной |
сброс); |
|||||
18 |
HANDLE canWriteEvent ; // все |
|
читатели |
готовы к |
при |
ему след |
ующего (автосброс); |
|
19 |
HANDLE allReadEvent ; // все ч |
итатели прочитали |
соо |
бщение ( |
ручной сброс); |
|||
20 |
HANDLE changeCountEvent ; // |
р |
азрешение работы с |
о с |
четчиком |
(автосброс); |
||
21 |
HANDLE exitEvent ; |
// завершение программы (ручной |
сброс); |
|
||||
22 |
|
|
|
|
|
|
|
|
23 // переменные для синхронизации работы потоков: |
|
|
|
|||||
24 |
int countread = 0; |
// число |
|
потоков , которое уже |
прочитали данные |
|||
25 // |
(устанавливается писателем |
и изменяется |
47
26 // |
читателями после прочтения сообщения) |
|
27 |
int countready = 0; // число потоков , готовых для чтения сообщения |
|
28 // |
(ожидающих сигнала от писателя) |
|
29 |
|
|
30 // имя разделяемой памяти |
||
31 |
wchar_t |
shareFileName [] = L" $$MyVerySpecialShareFileName$$ "; |
32 |
|
|
33HANDLE hFileMapping ; // объект -отображение файла
34// указатели на отображаемую память
35LPVOID lpFileMapForWriters ;
36LPVOID lpFileMapForReaders ;
37
38 int _tmain ( int argc , _TCHAR * argv []) {
39 Logger log ( _T (" ThreadsReaderWriter "));
40
41if ( argc < 2)
42// Используем конфигурацию по -умолчанию
43 |
SetDefaultConfig (& config , & log ); |
44else
45// Загрузка конфига из файла
46SetConfig ( argv [1] , & config , & log );
47 |
|
|
|
|
|
|
|
|
|
48 |
// создаем необходимые |
потоки без |
их |
запуска |
|
|
|||
49 |
CreateAllThreads (& config , |
& log ); |
|
|
|
|
|||
50 |
|
|
|
|
|
|
|
|
|
51 |
// Инициализируем ресурс ( share memory ): создаем |
объект |
"отображаемый файл" |
||||||
52 |
// будет использован системный |
файл |
подкачки (на диске |
файл создаваться |
|||||
53 |
// не будет) , т.к. в качестве дескриптора файла |
использовано значение |
|||||||
54 |
// равное 0 xFFFFFFFF (его |
эквивалент - символическая константа |
|||||||
|
INVALID_HANDLE_VALUE ) |
|
|
|
|
|
|
||
55 |
if (( hFileMapping = CreateFileMapping ( INVALID_HANDLE_VALUE , NULL , |
||||||||
56 |
PAGE_READWRITE , 0, 1500 , |
shareFileName )) == NULL ) { |
|
||||||
57 |
// INVALID_HANDLE_VALUE - дескриптор открытого файла |
|
|||||||
58 |
// |
|
|
( INVALID_HANDLE_VALUE - файл подкачки) |
|||||
59 |
// NULL - атрибуты защиты объекта -отображения |
|
|
||||||
60 |
// PAGE_READWRITE - озможности доступа к представлению файла при |
||||||||
61 |
// |
|
отображении ( PAGE_READWRITE - чтение/запись) |
||||||
62 |
// 0, 1500 |
- старшая |
и младшая |
части значения |
максимального |
||||
63 |
// |
размера объекта отображения файла |
|
64// shareFileName - имя объекта -отображения.
65log . loudlog ( _T (" Impossible to create shareFile , GLE = %d") ,
66GetLastError () );
67ExitProcess (10000) ;
68}
69// отображаем файл на адресное пространство нашего процесса для потока -писа
48
теля
70 lpFileMapForWriters = MapViewOfFile ( hFileMapping , FILE_MAP_WRITE , 0, 0, 0)
;
71// hFileMapping - дескриптор объекта -отображения файла
72// FILE_MAP_WRITE - доступа к файлу
73 |
// 0, 0 - старшая |
и |
младшая |
части |
смещения начала |
отображаемого участка в |
|||
|
файле |
|
|
|
|
|
|
|
|
74 |
// |
(0 - начало отображаемого участка |
совпадает с началом файла) |
||||||
75 |
// 0 - размер |
отображаемого участка файла в |
байтах (0 - весь файл) |
||||||
76 |
|
|
|
|
|
|
|
|
|
77 |
// отображаем файл на адресное пространство нашего |
процесса для потоков -чит |
|||||||
|
ателей |
|
|
|
|
|
|
|
|
78 |
lpFileMapForReaders |
= MapViewOfFile ( hFileMapping , |
FILE_MAP_READ , 0, 0, 0) ; |
||||||
79 |
|
|
|
|
|
|
|
|
|
80 |
// инициализируем средства синхронизации |
|
|
||||||
81 |
// (атрибуты защиты , автосброс , начальное состояние , имя): |
||||||||
82 |
// событие "окончание записи" (можно читать) , ручной сброс , изначально заня |
||||||||
|
то |
|
|
|
|
|
|
|
|
83 |
canReadEvent = CreateEvent ( NULL , true , false , L""); |
||||||||
84 |
// событие - "можно писать",автосброс(разрешаем писать только одному) , изна |
||||||||
|
чально свободно |
|
|
|
|
|
|
||
85 |
canWriteEvent = CreateEvent ( NULL , false , false , L""); |
||||||||
86 |
// событие "все прочитали" |
|
|
|
|
||||
87 |
allReadEvent = CreateEvent ( NULL , true , true , L""); |
|
|||||||
88 |
// событие для изменения счетчика (сколько клиентов еще не прочитало сообще |
||||||||
|
ние) |
|
|
|
|
|
|
|
|
89 |
changeCountEvent = CreateEvent ( NULL , false , true , L""); |
||||||||
90 |
// событие "завершение работы программы", ручной сброс , изначально занято |
||||||||
91 |
exitEvent = |
CreateEvent ( NULL , true , |
false , L""); |
|
|||||
92 |
|
|
|
|
|
|
|
|
|
93 |
// запускаем потоки -писатели и поток -планировщик на исполнение |
||||||||
94 |
for ( int i |
= 0; i |
< |
config . numOfReaders + config . numOfWriters + 1; i ++) |
|||||
95 |
ResumeThread ( allhandlers [i ]) ; |
|
|
|
|||||
96 |
|
|
|
|
|
|
|
|
|
97// ожидаем завершения всех потоков
98WaitForMultipleObjects ( config . numOfReaders + config . numOfWriters + 1,
99allhandlers , TRUE , INFINITE );
100 |
|
101 |
// закрываем handle потоков |
102 |
for ( int i = 0; i < config . numOfReaders + config . numOfWriters + 1; i ++) |
103 |
CloseHandle ( allhandlers [i ]) ; |
104 |
|
105// закрываем описатели объектов синхронизации
106CloseHandle ( canReadEvent );
107CloseHandle ( canWriteEvent );
49
108CloseHandle ( allReadEvent );
109CloseHandle ( changeCountEvent );
110CloseHandle ( exitEvent );
111
112// закрываем handle общего ресурса
113UnmapViewOfFile ( lpFileMapForReaders );
114UnmapViewOfFile ( lpFileMapForWriters );
115
116// закрываем объект "отображаемый файл"
117CloseHandle ( hFileMapping );
118 |
|
119 |
// Завершение работы |
120 |
log . loudlog ( _T (" All tasks are done !")); |
121 |
_getch () ; |
122 |
return 0; |
123 |
} |
Листинг 22: Единственный поток-писатель (src/SynchronizationPrimitives/ThreadsReaderWriter/threadWriter.cpp)
1 |
# include < windows .h > |
|
2 |
# include < stdio .h > |
|
3 |
# include |
< tchar .h > |
4 |
|
|
5 |
# include |
" utils .h" |
6 |
|
|
7 |
DWORD WINAPI ThreadWriterHandler ( LPVOID prm ) { |
|
8 |
int myid = ( int ) prm ; |
|
9 |
|
|
10Logger log ( _T (" ThreadsReaderWriter . ThreadWriter ") , myid );
11extern bool isDone ;
12extern struct Configuration config ;
13
14extern HANDLE canReadEvent ;
15extern HANDLE canWriteEvent ;
16extern HANDLE exitEvent ;
17
18extern int countread ;
19extern LPVOID lpFileMapForWriters ;
20
21int msgnum = 0;
22HANDLE writerhandlers [2];
23writerhandlers [0] = exitEvent ;
24writerhandlers [1] = canWriteEvent ;
25
50