Подтвердить что ты не робот

Синхронизация примитивов в .NET Framework: какая из них хорошая?

У меня проблема с пространством System.Threading Microsoft.NET. В этом пространстве имен многие классы определены, чтобы помочь мне управлять потоками. Ну, у меня проблема, но я не знаю, что использовать, MSDN расплывчата, и я до сих пор не понял, что делают классы. в частности, моя проблема связана с синхронизацией.

Проблема

У меня есть определенное количество потоков (рассмотрим N потоков). В определенный момент поток должен останавливаться и ждать, пока хотя бы один из других потоков ничего не сделает. Как только один из потоков N - 1 выполнил определенную задачу, этот поток уведомляет об этом и останавливается поток.

Итак, это всего лишь проблема синхронизации: поток должен ждать, чтобы его сигнализировали, что все.

Множество классов

В System.Threading существует множество классов, предназначенных для решения проблем синхронизации. Существуют WaitHandle (s), существуют AutoResetEvent (s), существуют ManualResetEvent (s) и т.д....

Какой я использую?

Вопрос

Мой вопрос: может ли кто-нибудь обобщить меня, какой класс я должен использовать, чтобы решить мою проблему? Не могли бы вы рассказать о самых важных различиях между этими классами или другими классами?

Дело в том, что я действительно не понял, на какой класс отвечает вопрос синхронизации: в чем разница, например, между WaitHandle и AutoResetEvent или ManualResetEvent?

Как насчет блокировки?

Для обработки многих проблем с потоками .net предоставляет функции lock и класс Monitor. Эта пара хороша для моих нужд?

Thankyou

4b9b3361

Ответ 1

Книга Альбахари удивительна, вы должны действительно прочитать ее через некоторое время. Его выросли в последнее время!

Что вы хотите

Вы хотите EventWaitHandle (EWH), они хороши, потому что ничего не обойти, они используются для сигнализации потоков ( либо в том же или в другом процессе), и, как следует из названия, их можно ожидать.

Как вы его используете

Вы откроете его в потоке, который выполняет ожидание, вы откроете его с заданным именем, о котором будет знать другой поток. Затем вы ждете этого дескриптора ожидания.

Сигнальная нить откроет существующий дескриптор ожидания с тем же именем (имя - строка) и вызовет set.

Различия

AutoResetEvent s и ManualResetEvent s оба наследуются от EWH, и они на самом деле просто EWH, они просто действуют по-другому. Какой из них вы хотите, просто зависит от того, хотите ли вы, чтобы EWH выступал в качестве ворот или турникетов. Это вам особенно заботит, если вы используете дескриптор wait более одного раза, или вы ожидаете этого дескриптора ожидания более чем одним потоком. Я использовал wait handle для приличного количества (я полагаю), и я не думаю, что когда-либо использовал руководство.

Важно знать

  • Независимо от того, что вы делаете, не передавайте экземпляр дескриптора ожидания, они должны быть открыты отдельно своими потоками. Указанное вами имя будет гарантировать, что они являются "одним и тем же" дескриптором ожидания.

  • Если потоки находятся в разных процессах, вы должны префикс имени EWH с помощью @"Global\", иначе имена дескрипторов ожидания будут инкапсулированы в один и тот же процесс. Кроме того, если вы используете их в одном и том же процессе, не используйте глобальное пространство имен. Если вы не укажете префикс с обратным слэшем, он автоматически добавится, чтобы сохранить его закрытым, но вам не нужно знать этот префикс.

  • Имейте в виду, что EWH может быть разрешен, и если у вас возникнут проблемы с этим, рекомендую использовать EventWaitHandleRights.FullControl, но вы можете просмотреть полный перечисление EventWaitHandleRights здесь.

  • Мне нравится называть EWH с Guid.NewGuid().ToString("N") (Guid.NewGuid и Guid.ToString). Обычно я делаю это, когда поток сигнализации создается, так как вы можете легко передать ему информацию в это время. Поэтому в этом случае начальный поток создает строку и передает ее в сигнальный поток при его создании. Таким образом, оба потока знают о названии, без необходимости делать какие-либо причудливые перекрестные потоки переменных.

  • EWH реализует IDisposable, чтобы обернуть его в using блок

Условия гонки

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

В связи с этим поток, ожидающий этого, должен будет иметь некоторое захват ошибок, потому что вам нужно будет вызвать OpenExisting. Если вы вызываете один из ctor 's, и EWH уже открыт, вы получите UnauthorizedAccessException или WaitHandleCannotBeOpenedException как описано здесь, в разделе Исключения. Вы все равно сможете открыть этот EWH и получить необходимую функциональность, вам просто нужно открыть его, а не создавать его.

Ответ 2

Разница между событием auto- reset и событием manual- reset заключается в том, что после того, как вы используете, автоматически удаляется (закрывается) событие auto-reset, поэтому через ворота проходит только один элемент. Я подозреваю, что AutoResetEvent будет здесь хорошо. Лично я склонен использовать Monitor больше, хотя - он имеет более низкие накладные расходы, но вам нужно быть немного осторожным; ваш первый поток должен обязательно иметь замок перед любым другим, т.е.

object lockObj = new object();
lock(lockObj) {
    // start the workers, making lockObj available to them

    Monitor.Wait(lockObj);
}

когда работники делают что-то вроде:

// lots of work
// now signal
lock(lockObj) Monitor.Pulse(lockObj);
// other work

Сохранение блокировки первоначально означает, что мы не пропускаем никаких сообщений, пока мы разворачиваем рабочих, так как любые рабочие, попадающие в lock(lockObj), будут заблокированы до тех пор, пока исходный поток не освободит блокировку в Monitor.Wait. Первый поток Pulse будет сигнализировать о продолжении нашего исходного потока.

Ответ 3

Существует отличная бесплатная электронная книга на эту тему (и проверьте часть 2)

Для чего использовать и когда на SO есть много тем, как этот: В чем разница между ManualResetEvent и AutoResetEvent в .NET?, процитировать Дэн Гольдштейн:

"Да, это похоже на разницу между платной дверью и дверью. ManualResetEvent - это дверь, которая должна быть закрыта (reset). AutoResetEvent - это tollbooth, позволяющая одному автомобилю проходить и автоматически закрываться до следующий может пройти".

Ответ 4

Вы можете использовать AutoResetEvent или ManualResetEvent. Единственное различие заключается в том, нужно ли вам позвонить Set() самостоятельно или сделать это.

Ответ 5

Может случиться, или это имеет значение, если "один из N-1 потоков выполнил определенную задачу" происходит до того, как "поток должен остановиться и ждать" достигает своей "определенной точки"? Это может повлиять на выбор синхронизма.

Rgds, Мартин