Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
делать №5.doc
Скачиваний:
3
Добавлен:
15.11.2019
Размер:
654.85 Кб
Скачать

Теоретичні відомості Мютекси

Поняття м’ютекса багато в чому збігається з поняттям блокування. Мютексом називають синхронізаційний примітив, що не допускає виконання деякого фрагменту коду більш як одним потоком. Фактично мютекс є реалізацією блокування на рівні ОС.

Мютекс, як випливає з його назви, реалізує взаємне виклю­чення. Його основне завдання — блокувати всі потоки, які нама­гаються отримати доступ до коду, коли цей код уже виконує деякий потік.

М’ютекс може бути у двох станах: вільному і зайнятому. Початковим станом є «вільний». Над м’ютексом є дві атомарні операції.

Зайняти м’ютекс (mutex_lock): якщо м’ютекс був вільний, він стає зайнятим, і потік продовжує своє виконання (входячи у критичну секцію); якщо м’ютекс був зайнятий, потік переходить у стан очікування (кажуть, що потік «очікує на м’ютексі», або «заблокований на м’ютексі»), виконання продовжує інший потік. Потік, який зайняв м’ютекс, називають власником м’ютексу (mutex owner):

mutex_lock (mutex_t mutex) {

if (mutex.state == free) {

mutex.state = locked;

mutex.owner = this_thread;

}

else sleep();

}

Звільнити м’ютекс (mutex_unlock): м’ютекс стає вільним; якщо на ньому очікує кілька потоків, з них вибирають один, він починає виконуватися, займає м’ютекс і входить у критичну секцію. У більшості реалізацій вибір потоку буде випадковим. Звільнити м’ютекс може тільки його власник. Ось псевдокод такої операції:

mutex_unlock (mutex_t mutex) {

if (mutex.owner != this_thread) return error;

mutex.state = free;

if (waiting_threads()) wakeup (some_thread);

}

Деякі реалізації надають ще третю операцію: спробувати зайняти м’ютекс (mutex_trylock): якщо м’ютекс вільний, діяти аналогічно до mutex_lock, якщо зайнятий — негайно повернути помилку і продовжити виконання.

Ось найпростіша реалізація критичної секції за допомогою м’ютексу.

mutex_t mutex;

mutex_lock(mutex);

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

mutex_unlock(mutex);

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

Правила спрощеного паралелізму

Правила спрощеного паралелізму (easy concurrency rules) призначені для спрощення програмування на базі м’ютексів. Вони ґрунтуються на тому очевидному факті, що м’ютекс захищає не код критичної секції, а спільно використовувані дані всередині цієї секції.

Кожна змінна, яку спільно використовує більш як один потік, має бути захищена окремим м’ютексом (скільки змінних, стільки м’ютексів):

volatile int i, data[100];

mutex_t i_mutex, data_mutex;

Перед кожною операцією зміни такої змінної відповідний м’ютекс має бути зайнятий, а після зміни звільнений:

mutex_lock(i_mutex);

i++;

mutex_unlock(i_mutex);

Якщо треба працювати одночасно із кількома спільно використовуваними змінними, необхідно зайняти всі їхні м’ютекси до початку роботи і звільнити їх тільки після повного закінчення роботи. Цим роботу розділяють на три етапи:

//зайняття м’ютексів

mutex_lock(i_mutex);

mutex_lock(data_lock);

//робота зі змінними

data[i++] = 100;

//звільнення м’ютексів

mutex_unlock(i_mutex);

mutex_unlock(data_mutex);

Зазначимо, що для спільно використовуваних даних задано клас пам’яті volatile. Використання такого модифікатора сповіщає компілятор мови С про можливе асинхронне змінення змінної поза даною програмою. Це завжди потрібно робити для спільно вико­ристовуваних даних.