Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
СП2_1 Синхронизация потоков.doc
Скачиваний:
0
Добавлен:
01.04.2025
Размер:
134.14 Кб
Скачать

13

Тема: Синхронизация потоков

Пример, иллюстрирующий необходимость синхронизации

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;

}