
Тема: Синхронизация потоков
Пример, иллюстрирующий необходимость синхронизации
DWORD a[5]; // Объявление глобального массива
// Функция потока 1
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{ int i; DWORD dwResult = 0, num=0;
struct PARAM *p= (struct PARAM *) pvParam;
while (!p->stop)
{
for ( i = 0; i < 5; i++ ) a[ i ] = num;
num++;
}
return(dwResult);
}
// Функция потока 2
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{ int i; DWORD dwResult = 0;
struct PARAM *p= (struct PARAM *) pvParam;
while (!p->stop)
{
for ( i = 0; i < 5; i++ ) printf("%ld ", a[ i ]);
printf("\n");
}
return(dwResult);
}
В результате, 1-й поток последовательно заполняет массив a[5] возрастающими сериями по 5 значений, а второй поток, работая параллельно, считывает и выводит на экран фрагменты этих серий чисел.
Средства синхронизации
- критический раздел (секция);
- семафор;
- мьютекс;
- событие;
- ожидаемый таймер.
Критические разделы (секции)
1.
CRITICAL_SECTION cs;
2.
VOID InitializeCriticalSection (LPCRITICAL_SECTION
lpCriticalSection);
Например: InitializeCriticalSection(&cs);
3.
VOID EnterCriticalSection (LPCRITICAL_SECTION
lpCriticalSection);
Например: EnterCriticalSection(&cs);
4.
VOID LeaveCriticalSection (LPCRITICAL_SECTION
lpCriticalSection);
Например: LeaveCriticalSection(&cs);
5.
VOID DeleteCriticalSection (LPCRITICAL_SECTION lpCriticalSection);
Например: DeleteCriticalSection(&cs);
Примечания.
1.
EnterCriticalSection(&cs);
// критичекий фрагмент кода потока
..............
LeaveCriticalSection(&cs);
2. Критические разделы используются для синхронизации двух и более потоков только внутри одного приложения, т.к. они не являются объектами ядра ОС.
Пример использования критического раздела
Синхронизируется работа двух потоков при заполнении элементов массива значениями и выводе их на экран.
CRITICAL_SECTION cs;
DWORD a[5]; // Объявление глобального массива
// Функция потока 1
DWORD WINAPI ThreadFunc1(PVOID pvParam)
{ int i; DWORD dwResult = 0, num=0;
struct PARAM *p= (struct PARAM *) pvParam;
while (!p->stop)
{
EnterCriticalSection(&cs);
for ( i = 0; i < 5; i++ ) a[ i ] = num;
LeaveCriticalSection(&cs);
num++;
}
return(dwResult);
}
// Функция потока 2
DWORD WINAPI ThreadFunc2(PVOID pvParam)
{ int i; DWORD dwResult = 0;
struct PARAM *p= (struct PARAM *) pvParam;
while (!p->stop)
{
EnterCriticalSection(&cs);
for ( i = 0; i < 5; i++ ) printf("%ld ", a[ i ]);
printf("\n");
LeaveCriticalSection(&cs);
}
return(dwResult);
}
// --- Функция WinMain
int APIENTRY WinMain(…)
{
// Создание объекта критический раздел
InitializeCriticalSection(&cs);
…
// Удаление объекта критический раздел
// (при необходимости)
DeleteCriticalSection(&cs);
…
}
Потоки по очереди заполняют и правильно выводят элементы массива сериями по 5 одинаковых значений.
Средства синхронизации потоков с использованием
объектов ядра
Функции ожидания потоков (Wait-функции)
1.
DWORD WaitForSingleObject (HANDLE hObject,
DWORD dwMilliseconds);
Возвращаемые значения:
WAIT_OBJECT_0 - объект освободился;
WAIT_TIMEOUT - время ожидания освобождения прошло, а объект не освободился;
WAIT_FAILED - произошла ошибка.
Например:
DWORD res = WaitForSingleObject(hProcess, 5000);
switch (res) {
case WAIT_OBJECT_0:
// процесс завершился
break;
case WAIT_TIMEOUT:
// процесс не завершился в течение 5000 мс
break;
case WAIT_FAILED:
// произошла ошибка
break;
}
2.
DWORD WaitForMultipleObjects(DWORD dwCount,
CONST HANDLE* phObjects, BOOL fWaitAll,
DWORD dwMilliseconds);
dwCount от 1 до MAXIMUM_WAIT_OBJECTS (64).
Возвращаемые значения:
- WAIT_TIMEOUT и WAIT_FAILED
- если параметр fWaitAll равен TRUE и все объекты перешли в свободное состояние, функция возвращает значение WAIT_OBJECT_0.
- если fWaitAll равен FALSE, функция возвращает управление, как только освобождается любой из объектов. При этом возвращается значение от WAIT_OBJECT_0 до WAIT_OBJECT_0 + dwCount - 1.
Таким образом, если возвращаемое значение не равно WAIT_TIMEOUT или WAIT_FAILED, то вычтя из него значение WAIT OBJECT_0, получим индекс в массиве описателей, который указывает, какой объект перешел в незанятое состояние. Например:
HANDLE h[3];
h[0] = hObject1;
h[1] = hObject2;
h[2] = hObject3;
DWORD res=WaitForMultipleObjects(3, h, FALSE, 3000);
switch (res) {
case WAIT_FAILED:
// произошла ошибка
break;
case WAIT_TIMEOUT:
// ни один из объектов не освободился
// в течение 3000 мс
break;
case WAIT_OBJECT_0 + 0:
// освободился объект с описателем hObject1
break;
case WAIT_OBJECT_0 + 1:
// освободился объект с описателем hObject2
break;
case WAIT_OBJECT_0 + 2:
// освободился объект с описателем hObject3
break;
}