Блокування повторного вступу в C #


119

Чи призведе до наступного коду тупик із використанням C # в .NET?

 class MyClass
 {
    private object lockObj = new object();

    public void Foo()
    {
        lock(lockObj)
        { 
             Bar();
        }
    }

    public void Bar()
    {
        lock(lockObj)
        { 
          // Do something 
        }
    }       
 }

6
Ми можемо розглянути можливість зміни назви цього питання - можливо, на щось подібне до нещодавно закритого Чому вкладені блокування не спричиняють тупик? Як видається, назва здається майже розробленою для того, щоб не дозволяти людям відкривати її.
Jeff Sternal

12
Насправді я знайшов це на основі пошукового слова "регент", і воно відповіло на моє запитання. Якщо це запитання щодо
дублювання

Я погоджуюся з коментарем @JeffSternal, це питання передбачає, що особа, яка шукає питання, вже знайома із замками "Перевласник" Ще одне запитання про дублювання, яке, на мою думку, мало для цього хороший заголовок: stackoverflow.com/questions/3687505/…
Луїс Перес

Відповіді:


148

Ні, до тих пір, поки ви не замикаєтесь на одному об’єкті. Рекурсивний код фактично вже має блокування і тому може безперешкодно тривати.

lock(object) {...}це скорочення для використання класу Monitor . Як вказує Марк , Monitorдопускається повторне введення в дію , тому неодноразові спроби блокування об'єкта, на якому в поточній нитці вже є замок, будуть працювати добре.

Якщо ви почнете фіксувати різні об’єкти, тоді потрібно бути обережним. Зверніть особливу увагу на:

  • Завжди купуйте замки на заданій кількості об'єктів в одній послідовності.
  • Завжди відпускайте замки у зворотному порядку, як ви їх придбали.

Якщо ви порушите будь-яке з цих правил, ви майже гарантовано отримаєте проблеми з тупиком в якийсь момент .

Ось одна гарна веб-сторінка, що описує синхронізацію потоків у .NET: http://dotnetdebug.net/2005/07/20/monitor-class-avoiding-deadlocks/

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

(Ще одне зауваження, ваш приклад не є технічно рекурсивним. Щоб він був рекурсивним, Bar()потрібно було б називати себе, як правило, частиною ітерації.)


1
Особливо в різних послідовностях.
Марк Гравелл

6
Повторна рекурсія; дійсно; на користь Гая, термін повторно вступає
Марк Гравелл

Дякую за роз’яснення щодо термінології - я відредагував і виправив питання.
Хлопець

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

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

20

Що ж, Monitorдозволяє повторне працевлаштування, тому ви не можете зайти в глухий кут ... так ні: це не повинно робити


7

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

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


Зауважте, що це стосується моніторів, але не обов'язково для інших видів блокування.
Джон Скіт

(Не бажаючи натякати на те, що ви цього не знали, звичайно - лише те, що це важлива відмінність :)
Джон Скіт

Це хороший момент. Я насправді збирався змінити "замок" на "монітор" протягом усього часу, але потім я відволікся. І ледачий. І така поведінка справедлива і для ядерних об’єктів mutex ядра, тому я зрозумів, досить близько!
Джефрі Л Уітлідж

5

Ні, цей код не матиме мертвих замків. Якщо ви дійсно хочете створити тупик, найпростіший потребує щонайменше 2 ресурсів. Розглянемо собачий і кістковий сценарій. 1. Собака має повний контроль над 1 кісткою, тому будь-яка інша собака повинна чекати. 2. 2 собаки з 2 кістками потрібно мінімально, щоб створити тупик, коли вони замикають кістки відповідно і також шукають інших кісток.

.. п. і т. п. собак і м кісток і спричиняють складніші тупики.

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