C#: монитор - ожидает, пульсирует, PulseAll

Библиотека Zip DotNet (Ionic.Zip.dll) очень проста в использовании. Я чувствую, что легче реализовать, чем SharpZip.

http://www.codeplex.com/DotNetZip

47
задан Brian Rasmussen 15 June 2011 в 21:24
поделиться

3 ответа

Краткая версия:

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 , чтобы вы могли хотя бы выйти из блокировки (позволяя пользовательскому интерфейсу играть).


Подождите / Pulse / PulseAll разные; они предназначены для сигнализации. Я использую этот в этом ответе , чтобы сигнализировать о том, что:

  • Dequeue : если вы попытаетесь удалить данные из очереди, когда очередь пуста, он ждет, пока другой поток добавит данные, что пробуждает заблокированный поток
  • Enqueue : если вы попытаетесь поставить данные в очередь, когда очередь заполнена, он ждет, пока другой поток удалит данные, который пробуждает заблокированный поток

Pulse пробуждает только один поток, но я недостаточно умен, чтобы доказать, что следующий поток всегда тот, который я хочу, поэтому я обычно использую PulseAll и просто повторно проверьте условия перед продолжением; в качестве примера:

        while (queue.Count >= maxSize)
        {
            Monitor.Wait(queue);
        }

При таком подходе я могу безопасно добавлять другие значения Pulse без того, чтобы мой существующий код предполагал, что «Я проснулся, следовательно, есть данные» - что удобно, когда (в тот же пример) Позже мне понадобилось добавить метод Close () .

54
ответ дан 26 November 2019 в 19:28
поделиться

Нет, они не защищают вас от тупиковых ситуаций. Это просто более гибкие инструменты для синхронизации потоков. Вот очень хорошее объяснение того, как их использовать, и очень важный шаблон использования - без этого шаблона вы все сломаете: http://www.albahari.com/threading/part4.aspx

10
ответ дан 26 November 2019 в 19:28
поделиться

They are tools for synchronizing and signaling between threads. As such they do nothing to prevent deadlocks, but if used correctly they can be used to synchronize and communicate between threads.

Unfortunately most of the work needed to write correct multithreaded code is currently the developers' responsibility in C# (and many other languages). Take a look at how F#, Haskell and Clojure handles this for an entirely different approach.

1
ответ дан 26 November 2019 в 19:28
поделиться
Другие вопросы по тегам:

Похожие вопросы: