Блокування повторного вступу в 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.