Страницы

Поиск по вопросам

понедельник, 3 июня 2019 г.

Возможно ли снять блокировку из другого потока?

При попытке снять блокировку монитора из другого потока выбрасывается исключение
An unhandled exception of type 'System.Threading.SynchronizationLockException' occurred in Test - Smth.exe Additional information: Для не синхронизированного блока кода вызван метод синхронизации объектов.
Пример кода:
Imports System.Threading
Module All Sub Main() Dim Obj As New Object
Monitor.Enter(Obj) Call (New Thread(Sub() Monitor.Exit(Obj))).Start()
Console.ReadKey() End Sub End Module
Возможно ли как-то снять блокировку из того потока, который её НЕ устанавливал?


Ответ

С монитором — нет, никак.
Насколько я понимаю, единственный синхронизационный примитив, который позволяет разблокировать себя из другого потока — это Semaphore (лучше брать рекомендованный вариант SemaphoreSlim).
Пример (на C#)
var sema = new SemaphoreSlim(0); // создаём заблокированный семафор new Thread(() => { sema.Release(); // разблокируем его // ... }).Start(); sema.Wait(); // ожидаем разблокировки
Если вы хотите просто дождаться реального старта другого потока, можно использовать ManualResetEventSlim
Код практически такой же:
var ev = new ManualResetEventSlim(initialState: false); new Thread(() => { ev.Set(); // ... }).Start(); ev.Wait();

Поправка: всё-таки исходную проблему можно решить с монитором, если заставить его работать как conditional variable. Код получается немного более сложным, и требует вспомогательной булевой переменной:
bool started = false; object mutex = new object(); new Thread(() => { lock (mutex) { started = true; Monitor.Pulse(mutex); } // ... }).Start();
lock (mutex) { while (!started) Monitor.Wait(mutex); // блокировка будет отпущена на период ожидания }
И наконец, если не только основному потоку нужно дождаться порождённый поток, но и наоборот, порождённый поток должен задержаться до тех пор, пока основной поток не узнает, что он стартовал, у нас возникает так называемая задача рандеву: синхронизация одной точки выполнения у нескольких потоков. Это проще всего делать при помощи Barrier:
var barrier = new Barrier(participantCount: 2); new Thread(() => { barrier.SignalAndWait(); // (*) // ... }).Start();
barrier.SignalAndWait(); // (*)
Строки, отмеченные звёздочкой, будут ждать друг друга: первый из пришедших в эту строку потоков затормозится до прихода второго.

Комментариев нет:

Отправить комментарий