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

5.3.2. М'ютекси

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

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

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

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

mutex_lock (mutex_t mutex) {

if (mutex.state == free) {

mutex.state = loked;

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) [78] призначені для спро­щення програмування на базі м'ютексів. Вони ґрунтуються на тому очевидному факті, що м'ютекс захищає не код критичної секції, а спільно використовувані да­ні всередині цієї секції.

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

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_ mutex):

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

data[i++] = 100:

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

mutex_unlock(i_ mutex): mutex_unlock(data_ mutex):

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]