Особливості багатозадачності в середовищі Windows

Контрольная работа - Компьютеры, программирование

Другие контрольные работы по предмету Компьютеры, программирование

хронізації він не допоможе. На щастя у Windows передбачено пять стандартних механізмів для синхронізації процесів і потоків:

семафор

критична секція

мютекс

подія

таймер

Розглянемо кожний з цих механізмів.

 

Критична секція

 

Критична секція - це частина коду, доступ до якого тепер має тільки один потік. Інший потік може звернутися до критичного розділу, тільки коли перший вийде з нього.

Для роботи з критичними секціями використовуються наступні функції:

VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection) - ініціалізація синхронізатора типу критичний розділ.

lpCriticalSection - покажчик на змінну типу CRITICAL_SECTION.

VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection) - запит на вхід у критичну секцію(розділ)

lpCriticalSection - покажчик на змінну типу CRITICAL_SECTION.

VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection) - вихід із критичного розділу (звільнення семафора).

lpCriticalSection - покажчик на змінну типу CRITICAL_SECTION.

VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection) - видалення критичного розділу (звичайно при виході з програми).

lpCriticalSection - покажчик на змінну типу CRITICAL_SECTION.

Отже, для створення критичного розділу необхідно ініціалізувати структуру CRITICAL_SECTION. Що Windows у цій структурі зберігає, нас не стосується - важливо, що покажчик на цю структуру ідентифікує наш семафор.

Створивши обєкт CRITICAL_SECTION, ми можемо працювати з ним, тобто можемо позначити код, доступ до якого для одночасно виконуються задач потрібно синхронізувати.

Розглянемо такий приклад. Ми хочемо записувати і зчитувати значення з деякого глобального масиву mas. Причому запис і зчитування повинні вироблятися двома різними потоками. Цілком природно, що краще якщо ці дії не будуть виконуватися одночасно. Тому введемо обмеження на доступ до масиву.

І хоча приведений нами приклад подібного обмеження (см. лістинг 1)надзвичайно спрощений, він добре ілюструє роботу синхронізатора типу критичний розділ: поки один потік "володіє" масивом, інший доступу до нього не має.

Мютекси (взаємовиключення)

 

Мютекс (взаємовиключення, mutex) - це обєкт синхронізації, який установлюється в особливий сигнальний стан, коли не зайнятий яким-небудь потоком. Тільки один потік володіє цим обєктом в любий момент часу, звідси и назва таких обєктів одночасний доступ до спільного ресурсу виключається. Наприклад, щоб виключити запис двох потоків в спільний участок памяті в один і то й же час, кожний потік очікує, коли звільниться мютекс, стає його власником и тільки потім пише щось в цю ділянку памяті. Після всіх необхідних дій мютекс звільняється, надаючи іншим потокам доступ до спільного ресурсу.

Два (або більше) процесів можуть створити мютекс з одним і тим же іменем, визвавши метод CreateMutex. Перший процес дійсно створює мютекс, а наступні процеси отримують хендл існуючого вже обєкта. Це дає можливість декільком процесам отримати хендл одного і того ж мютекса, звільняючи програміста від необхідності турбуватися про те, хто насправді створює мютекс. Якщо використовується такий підхід, бажано встановити флаг bInitialOwner в FALSE, інакше виникнуть певні труднощі при визначенні справжнього “творця” мютекса.

Декілька процесів можуть отримати хендл (handle) одного й того ж мютекса, що робить можливим взаємодію між процесами. Ви можете використовувати наступні механізми такого підходу:

Дочірній процес, створений за допомогою функції CreateProcess може наслідувати хендл мютекса у випадку, якщо при його (мютекса) створенні функцією CreateMutex був вказаний параметр lpMutexAttributes.

Процес може отримати дублікат існуючого мютекса з допомогою функції DuplicateHandle.

Процес може вказати імя існуючого мютекса при виклику функцій OpenMutex або CreateMutex.

#include

#include

#include

HANDLE hMutex;

int a[ 5 ];

void Thread(void* pParams)

{

int i, num = 0;

while (TRUE)

{

WaitForSingleObject(hMutex, INFINITE);

for (i = 0; i < 5; i++) a[ i ] = num;

ReleaseMutex(hMutex);

num++;

}

}

int main(void)

{

hMutex = CreateMutex(NULL, FALSE, NULL);

_beginthread(Thread, 0, NULL);

while(TRUE)

{

WaitForSingleObject(hMutex, INFINITE);

printf("%d %d %d %d %d\n",

a[ 0 ], a[ 1 ], a[ 2 ],

a[ 3 ], a[ 4 ]);

ReleaseMutex(hMutex);

}

return 0;

}

Як видно з результату роботи процесу, основний потік (сама програма) і потік hMutex дійсно працюють паралельно (червоним кольором позначений стан, коли основний потік виводить масив під час його заповнення потоком hMutex):

 

81751652 81751652 81751651 81751651 81751651

81751652 81751652 81751651 81751651 81751651

83348630 83348630 83348630 83348629 83348629

83348630 83348630 83348630 83348629 83348629

83348630 83348630 83348630 83348629 83348629

 

Приклад. Допустимо, в програмі використовується ресурс, наприклад, файл або буфер в памяті. Функція WriteToBuffer() викликається з різних потоків. Щоб уникнути колізій при одночасному зверненні до буферу з різних потоків, використовуємо мютекс. Перед тим як звернутися до буфера, очікуємо „звільнення” мютекса.

 

HANDLE hMutex;

int main()

{

hMutex = CreateMutex(NULL, FALSE, NULL); // Создаем мьютекс в свободном состоянии

//...

// Создание потоков, и т.д.

//...

}

BOOL WriteToBuffer()

{DWORD dwWaitResult;

// Ждем освобождения мьютекса перед тем как обратиться к буферу.

dwWaitResult = WaitForSingleObject(hMutex, 5000L); // 5 секунд на таймаут

if (dwWaitResult == WAIT_TIMEOUT) // Таймаут. Мьютекс за єто время не освободился.

{

return FALSE;

}

else // Мьютекс освободился, и наш поток его занял. Можно работать.

{

Write_to_the_buffer().

...

ReleaseMutex(hMutex); // Освобождаем мьютекс.

}

return TRUE;

 

Семафор

 

Ще один вид синхронізаторів - семафор, що виключає. Основна його відмінність від критичної секції по?/p>