Особливості багатозадачності в середовищі 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>