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

AutoResetEvent против boolean, чтобы остановить поток

У меня есть объект в рабочем потоке, который я могу дать инструкции прекратить работу. Я могу реализовать это с помощью bool или AutoResetEvent:

булева:

private volatile bool _isRunning;

public void Run() {
    while (_isRunning)
    {
        doWork();
        Thread.Sleep(1000);
    }
}

AutoResetEvent:

private AutoResetEvent _stop;

public void Run() {
    do {
        doWork();
    } while (!_stop.WaitOne(1000));
}

Затем метод Stop() установил _isRunning в значение false или вызовет _stop.Set().

Кроме того, решение с AutoResetEvent может немного ускориться, есть ли разница между этими методами? Является ли это "лучше", чем другой?

4b9b3361

Ответ 1

С# volatile не предоставляет всех гарантий. Он все равно может читать устаревшие данные. Лучше использовать базовый механизм синхронизации ОС, поскольку он обеспечивает гораздо более надежные гарантии.

Все это в большой глубине обсуждается Эриком Липпертом (действительно стоит прочитать), вот короткая цитата:

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

Собственно, этот последний бит - ложь. Истинная семантика неустойчивых чтений и записи значительно сложнее, чем я описал здесь; в fact они фактически не гарантируют, что каждый процессор останавливает то, что он делает и обновляет кеши в/из основной памяти. Скорее, они предоставляют более слабые гарантии того, как доступ к памяти до и после чтения и можно наблюдать, что порядок упорядочен относительно друг друга.Некоторые операции, такие как создание нового потока, ввод блокировки или использование одного из семейств методов Interlocked гарантии соблюдения порядка заказа. Если вы хотите получить более подробную информацию, прочитайте разделы 3.10 и 10.5.3 спецификации С# 4.0.

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

Ответ 2

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

Так что действительно важно только, как быстро поток будет отвечать на запрос остановки. Легко измерить, просто запустите секундомер в потоке управления и запишите время после цикла while в рабочем потоке. Результаты, которые я измерил от повторения, принимали 1000 образцов и принимали среднее значение, повторяли 10 раз:

volatile bool, x86:         550 nanoseconds
volatile bool, x64:         550 nanoseconds
ManualResetEvent, x86:     2270 nanoseconds
ManualResetEvent, x64:     2250 nanoseconds
AutoResetEvent, x86:       2500 nanoseconds
AutoResetEvent, x64:       2100 nanoseconds
ManualResetEventSlim, x86:  650 nanoseconds
ManualResetEventSlim, x64:  630 nanoseconds

Помните, что результаты для volatile bool вряд ли будут хорошо смотреться на процессоре со слабой моделью памяти, такой как ARM или Itanium. У меня нет возможности проверить.

Очевидно, похоже, что вы хотите одобрить ManualResetEventSlim, предоставляя хороший перфект и гарантию.

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

Ответ 3

IMHO AutoResetEvent лучше, потому что вы не можете забыть ключевое ключевое слово volatile в этом случае.

Ответ 4

Прежде чем использовать ключевое слово volatile, вы должны прочитать this и, я думаю, при исследовании многопоточности вы могли бы прочитать весь http://www.albahari.com/threading/.

Он объясняет тонкости ключевого слова volatile и почему его поведение может быть неожиданным.


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


После просмотра, я не думаю, что ваш код работает по нескольким причинам,

Фрагмент "boolean:" всегда спит примерно на секунду, возможно, не то, что вы хотите.

Фрагмент "AutoResetEvent:" не создает экземпляр _stop и всегда будет запускать doWork() хотя бы один раз.

Ответ 5

Это зависит от того, является ли вышеприведенный фрагмент всего, что вы делаете, или нет. Событие AutoReset, как следует из его названия, получает reset после передачи WaitOne. Это означает, что вы можете использовать его снова сразу, а не с помощью bool, чтобы вернуть его к истинному.