Чому більшість імплементацій mutex несправедливі?


11

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

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

Причина, чому мутекси, як правило, не реалізуються, щоб бути справедливими, - це "ефективність", але я хотів би краще зрозуміти, що це означає - зокрема, як розслаблення вимог щодо справедливості мутексу покращує ефективність? Здається, що "справедливий" мьютекс було б тривіально реалізовувати - просто встановіть lock (), додайте викликову нитку до хвоста списку пов'язаних файлів mutex перед тим, як покласти нитку в режим сну, а потім розблокувати () спливати наступну нитку з голова того самого списку і розбудити його.

Яку інформацію про впровадження мютексу я тут не пропускаю, це пояснювало б, чому вважалося доцільним жертвувати справедливістю для кращої роботи?


1
Цей зв'язаний список для кожного мутексу повинен бути спільною структурою даних, правда? Отже, як ви будете запобігати перегонам даних на цьому, не знижуючи продуктивність?
користувач3543290

Я думаю, використовуючи механізм безперервного зв'язку, пов'язаний із списком. Яку структуру даних використовує несправедливий мютекс, щоб знайти наступний потік для пробудження?
Джеремі Фріснер

1
Вам доведеться це переглянути, але чи гарантує справедливість безперебійний список? Думаю, ви знайдете такі гарантії, як справедливість у паралельному програмуванні.
користувач3543290

Відповіді:


7

Відповідь Джима Сойєра вказує на одну відповідь: Якщо у вас є теми з різними пріоритетами, "справедлива" поведінка була б неправильною. Коли у вас є кілька потоків, які можуть працювати, найвищий пріоритетний потік, як правило, той, який повинен запускатися.

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

Типовим прикладом є обробник сигналу в Unix, асинхронна системна пастка у VMS або асинхронний виклик процедури в Windows NT. Все це по суті одне і те ж: Операційна система повинна повідомити користувальницький процес про те, що сталася якась подія, і це обробляється за допомогою запуску коду в просторі користувача.

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

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


4

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


Ласкаво просимо на сайт і дякуємо за відповідь на питання, яке сидить довгий час без відповідей! Ваша відповідь, звичайно, правильна, але я вважаю, що вона може мати трохи більше деталей.
Девід Річербі

3

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

І якщо випускаюча нитка негайно намагається знову придбати ту саму мутекс, вона повинна поставити себе в тил черги офіціанта і перейти спати. Це не сталося б, якби нитка не випустила мютекс для початку. Тому це стимулює довші "жадібніші" критичні розділи.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.