У меня трудное время в понимании Wait()
, Pulse()
, PulseAll()
. Все ли они избегут взаимоблокировки? Буду признателен, если вы объясните, как их использовать?C#: Монитор - Подождите, Импульс, PulseAll
ответ
Короткая версия:
lock(obj) {...}
коротка рукой для Monitor.Enter
/Monitor.Exit
(с обработкой исключений и т.д.). Если никто не имеет блокировки, вы можете получить его (и запустить свой код), иначе ваш поток будет заблокирован до тех пор, пока блокировка не будет приобретена (другим потоком, освобождающим его).
Тупик обычно не происходит, когда либо A: две нити запирать вещи в различных порядках:
thread 1: lock(objA) { lock (objB) { ... } }
thread 2: lock(objB) { lock (objA) { ... } }
(здесь, если каждый из них приобретают первый замок, ни может никогда получить второе, так как ни один поток может выход для снятия их блокировки)
Этот сценарий можно свести к минимуму, всегда фиксируя его в том же порядке; и вы можете восстановить (в степени) с помощью Monitor.TryEnter
(вместо Monitor.Enter
/lock
) и указав таймаут.
или B: вы можете заблокировать себя с вещами, как WinForms, когда нить переключение, удерживая блокировку:
lock(obj) { // on worker
this.Invoke((MethodInvoker) delegate { // switch to UI
lock(obj) { // oopsiee!
...
}
});
}
Тупик кажется очевидным выше, но это не так очевидно, когда у вас есть спагетти кода; Возможные ответы: не переключайте нить при удерживании блокировок или используйте BeginInvoke
, чтобы вы могли хотя бы выйти из блокировки (позволяя играть в интерфейс).
Wait
/Pulse
/PulseAll
различны; они предназначены для сигнализации.Я использую этот in this answer сигнализировать таким образом, чтобы:
Dequeue
: если вы пытаетесь из очереди данных, когда очередь пуста, она ожидает другой поток, чтобы добавить данные, которые будят блокированный потокEnqueue
: если вы пытаетесь поставить в очередь данные, когда очередь заполнена, она ждет другого потока, чтобы удалить данные, которые будит заблокированное нить
Pulse
только будит один нить - но я не достаточно башковитый, чтобы доказать что следующий поток i всегда я хочу, поэтому я предпочитаю использовать PulseAll
и просто повторю проверку условий перед продолжением; В качестве примера:
while (queue.Count >= maxSize)
{
Monitor.Wait(queue);
}
С таким подходом, я могу смело добавить и другие значения из Pulse
, без моего существующего кода при условии, что «я просыпался, поэтому есть данные» - что очень удобно, когда (в том же примере) Мне позже нужно было добавить метод Close()
.
Прочитано Jon Skeet's multi-part threading article.
Это действительно хорошо. Вы упомянули примерно треть пути.
Нет, они не защищают вас от тупиков. Это просто более гибкие инструменты для синхронизации потоков. Вот очень хорошее объяснение, как использовать их и очень важный образец использования - без этого шаблона вы сломаете все вещи: http://www.albahari.com/threading/part4.aspx
ДЛЯ NOVICES ВЫСОКО РЕКОМЕНДУЕТ страницу albahari! Просматривает потоки и синхронизирует шаг за шагом с ясными примерами. – DanG
Это инструменты для синхронизации и сигнализации между потоками. Таким образом, они не делают ничего, чтобы предотвратить взаимоблокировки, но если они используются правильно, они могут использоваться для синхронизации и обмена данными между потоками.
К сожалению, большая часть работы, необходимой для написания правильного многопоточного кода, в настоящее время является ответственностью разработчиков на C# (и многих других языках). Посмотрите, как F #, Haskell и Clojure обрабатывают это для совершенно другого подхода.
Простой рецепт использования Monitor.Wait и Monitor.Pulse. Она состоит из рабочих, босса, и телефона они используют для общения:
object phone = new object();
"Рабочая" Нить:
lock(phone) // Sort of "Turn the phone on while at work"
{
while(true)
{
Monitor.Wait(phone); // Wait for a signal from the boss
DoWork();
Monitor.PulseAll(phone); // Signal boss we are done
}
}
A "Boss" резьба:
PrepareWork();
lock(phone) // Grab the phone when I have something ready for the worker
{
Monitor.PulseAll(phone); // Signal worker there is work to do
Monitor.Wait(phone); // Wait for the work to be done
}
Более сложные примеры следуют ...
«Работник с чем-то другим заниматься»:
lock(phone)
{
while(true)
{
if(Monitor.Wait(phone,1000)) // Wait for one second at most
{
DoWork();
Monitor.PulseAll(phone); // Signal boss we are done
}
else
DoSomethingElse();
}
}
"Нетерпеливый Boss":
PrepareWork();
lock(phone)
{
Monitor.PulseAll(phone); // Signal worker there is work to do
if(Monitor.Wait(phone,1000)) // Wait for one second at most
Console.Writeline("Good work!");
}
Я не понимаю. Как возможно, что Босс и Рабочий находятся в «телефонной» блокировке одновременно? – Marshall
@Marshall Monitor.Wait выпускает блокировку «телефона» на следующую строку (предположительно на Boss). –
@DennisGorelik ahh, я вижу. Я расширил ваш пункт [ниже] (http://stackoverflow.com/a/42581381/1282864) – jdpilgrim
К сожалению, ни из Wait(), Pulse() или PulseAll() имеют волшебное свойство, которое вы желающему за - что что используя этот API, вы автоматически избегаете взаимоблокировки.
Рассмотрим следующий код
object incomingMessages = new object(); //signal object
LoopOnMessages()
{
lock(incomingMessages)
{
Monitor.Wait(incomingMessages);
}
if (canGrabMessage()) handleMessage();
// loop
}
ReceiveMessagesAndSignalWaiters()
{
awaitMessages();
copyMessagesToReadyArea();
lock(incomingMessages) {
Monitor.PulseAll(incomingMessages); //or Monitor.Pulse
}
awaitReadyAreaHasFreeSpace();
}
Этот код будет тупиковой! Может быть, не сегодня, может быть, не завтра. Скорее всего, когда ваш код находится под стрессом, потому что внезапно он стал популярным или важным, и вы вызываетесь, чтобы исправить неотложную проблему.
Почему?
В конце концов произойдет следующее:
- Всех потребительские потоки делают некоторую работу
- сообщений приходят, готовая площадь не может вместить больше никаких сообщений, и PulseAll() называется.
- Нет потребитель получает проснулся, потому что никто не ждет
- Все потребительские потоки называют Wait() [ТУПИК]
Этот частный пример предполагает, что производитель нить никогда не будет называть PulseAll() еще раз потому что у него больше нет места для ввода сообщений. Но есть много, много Возможные варианты нарушения этого кода. Люди будут пытаться сделать его более надежным путем изменения линии, такие как создание Monitor.Wait();
в
if (!canGrabMessage()) Monitor.Wait(incomingMessages);
К сожалению, до сих пор не достаточно, чтобы исправить это. Чтобы исправить это также необходимость изменения стопорное сферы, где Monitor.PulseAll()
называется:
LoopOnMessages()
{
lock(incomingMessages)
{
if (!canGrabMessage()) Monitor.Wait(incomingMessages);
}
if (canGrabMessage()) handleMessage();
// loop
}
ReceiveMessagesAndSignalWaiters()
{
awaitMessagesArrive();
lock(incomingMessages)
{
copyMessagesToReadyArea();
Monitor.PulseAll(incomingMessages); //or Monitor.Pulse
}
awaitReadyAreaHasFreeSpace();
}
Ключевым моментом является то, что в фиксированном коде, замки ограничивают возможные последовательности событий:
а потребительские нити делает свою работу и петли
Этот поток получает блокировку
И благодаря блокировке в настоящее время верно, что либо:
a. Сообщения еще не прибыли в готовой области, и он снимает блокировку с помощью вызова Wait() перед резьбой приемника сообщения может получить блокировку и скопировать несколько сообщений в готовую область, или
б. Сообщения имеют уже прибыл в готовую область и получает сообщения INSTEAD OF вызова Wait(). (И в то время как он делает это решение невозможно для приемника сообщений, чтобы, например, получить блокировку и скопировать несколько сообщений в готовую область.)
В результате проблема исходного кода в настоящее время не происходит : 3. Когда PulseEvent() называется нет потребитель получает проснулся, потому что никто не ждет
Теперь заметим, что в этом коде вы должны получить фиксирующую сферу именно право. (Если, конечно, я получил это право!)
А также, так как вы должны использовать lock
(или Monitor.Enter()
и т.д.) для того, чтобы использовать Monitor.PulseAll()
или Monitor.Wait()
в тупиковой свободной моды, вам все равно придется беспокоиться о возможности другие взаимоблокировки, которые происходят из-за этого блокировки.
Итог: эти API, также легко завинтить и тупиковая с, т.е. довольно опасно
то, что общий бросил меня в том, что Pulse
просто дает «головы», чтобы нить в Wait
,Нить ожидания не будет продолжаться до тех пор, пока нить, которая сделала Pulse
, не выдает блокировку, и ожидающая нить успешно побеждает ее.
lock(phone) // Grab the phone
{
Monitor.PulseAll(phone); // Signal worker
Monitor.Wait(phone); // ****** The lock on phone has been given up! ******
}
или
lock(phone) // Grab the phone when I have something ready for the worker
{
Monitor.PulseAll(phone); // Signal worker there is work to do
DoMoreWork();
} // ****** The lock on phone has been given up! ******
В обоих случаях это не не до «замок на телефоне было отказано», что другой поток может получить его.
Могут быть другие темы, ожидающие этого замка от Monitor.Wait(phone)
или lock(phone)
. Только тот, кто выигрывает замок, будет продолжать.
- 1. Внутренние элементы «Wait», «Pulse», «PulseAll»
- 2. C#: импульс и ожидание
- 3. C++ импульс Десериализация подбрасывает ошибки
- 4. Монитор Факс с C#
- 5. C# Монитор и обновление
- 6. C# выборочно отключить монитор
- 7. C# Монитор отсутствует файл
- 8. C# параллелизм - Монитор
- 9. C#: Подождите, пока переменная станет ненулевой.
- 10. Как заменить этот семафор на монитор?
- 11. монитор с перегрузкой оператора C++
- 12. Монитор исключений C# с фильтрацией
- 13. Зачем нужен монитор, прежде чем звонить Monitor.Wait()
- 14. Процесс Подождите, пока Start C#
- 15. Подождите всех задач C# MVC
- 16. Монитор активности
- 17. Линейный импульс Box2D?
- 18. Повторить импульс Анимация
- 19. завершение бегущего импульс нить
- 20. Применить импульс не работает
- 21. новообращенный в струнной импульс
- 22. импульс shared_ptr интерфейс/объект
- 23. Пусковой импульс от центра
- 24. Ускорение или импульс
- 25. Не удалось создать импульс
- 26. Как сделать прыгающий импульс
- 27. импульс для каждой задачи
- 28. Sprite комплект импульс в точке
- 29. Как нарисовать монитор сердечного ритма на Android
- 30. Threading.Monitor - несколько объектов блокировки vs PulseAll - что является более ресурсоэффективным?
он даже не упоминает пульс или не подождет! Ссылка на статью John Skeet не дает ответ автоматически ... –
О, действительно? Что это? 'http: // www.yoda.arachsys.com/csharp/threads/deadlocks.shtml' – Geo
@Geo: да, этот подходит лучше;) –