2010-05-12 3 views
5

Я написал, что я надеюсь, является легкой альтернативой использованию классов ManualResetEvent и AutoResetEvent в C# /. NET. Обоснование этого заключалось в том, чтобы иметь функциональность «Событие» без веса использования объекта блокировки ядра.Легкая альтернатива ручному/AutoResetEvent в C#

Хотя код, похоже, хорошо работает как при тестировании, так и в производстве, получение такого рода прав для всех возможностей может быть чреватым делом, и я смиренно прошу о любых конструктивных комментариях и критике со стороны StackOverflow. Надеюсь (после обзора) это будет полезно для других.

Использование должно быть аналогично классам Manual/AutoResetEvent с Notify(), используемым для Set().

Здесь идет:

using System; 
using System.Threading; 

public class Signal 
{ 
    private readonly object _lock = new object(); 
    private readonly bool _autoResetSignal; 
    private bool _notified; 

    public Signal() 
    : this(false, false) 
    { 
    } 

    public Signal(bool initialState, bool autoReset) 
    { 
    _autoResetSignal = autoReset; 
    _notified = initialState; 
    } 

    public virtual void Notify() 
    { 
    lock (_lock) 
    { 
     // first time? 
     if (!_notified) 
     { 
     // set the flag 
     _notified = true; 

     // unblock a thread which is waiting on this signal 
     Monitor.Pulse(_lock); 
     } 
    } 
    } 

    public void Wait() 
    { 
    Wait(Timeout.Infinite); 
    } 

    public virtual bool Wait(int milliseconds) 
    { 
    lock (_lock) 
    { 
     bool ret = true; 
     // this check needs to be inside the lock otherwise you can get nailed 
     // with a race condition where the notify thread sets the flag AFTER 
     // the waiting thread has checked it and acquires the lock and does the 
     // pulse before the Monitor.Wait below - when this happens the caller 
     // will wait forever as he "just missed" the only pulse which is ever 
     // going to happen 
     if (!_notified) 
     { 
     ret = Monitor.Wait(_lock, milliseconds); 
     } 

     if (_autoResetSignal) 
     { 
     _notified = false; 
     } 
     return (ret); 
    } 
    } 
} 
+1

протестированных Вы это против использования ManualResetEvent/AutoResetEvents? Насколько значительна разница в производительности? – James

+0

Нет. Я еще не был, поскольку основной целью было создание нечерного дескриптора/ресурса с использованием объекта события. Спасибо, но я попытаюсь настроить некоторые тесты. – sweetlilmre

+1

Реализация собственных потоковых примитивов - это реализация ваших собственных криптографических алгоритмов. Если вы не являетесь экспертом в домене, вы * будете его испортить. Даже если вы * эксперт, вы все еще можете его испортить. Не делай этого. .NET 4 уже имеет «облегченные» версии в любом случае, «ManualResetEventSlim» и связанные с ними классы. – Aaronaught

ответ

4

Это работает из предположения, что Win32 события являются дорогостоящими. Их нет, мало что можно представить, это дешевле, чем событие. Главный намек на то, что это так, дизайнеры .NET решили, что было бы неплохо использовать событие Win32 для реализации MRE и ARE.

Истинная стоимость вашей замены - это основной FUD, который вы испытаете, когда у вас есть гонка с резьбой и не знаете, что ее вызывает.

+1

. Предполагается, что ARE и MRE используют событие ядра под капотом, которое было бы дороже, чем монитор. Я хотел бы, чтобы вас доказали неправильно :) – sweetlilmre

+0

В этой статье подробно описаны Microsoft, которые быстро: http://msdn.microsoft.com/en-us/library/ms228964.aspx –

+0

С точки зрения перехода в режиме ядра в .NET события Win32 являются дорогостоящими, поэтому легкие дополнения в .NET 4 в соответствии с ответом @Aaronaught выше. – sweetlilmre

1

К сожалению, правильная реализация монитора довольно тяжелая, учитывая примитивы синхронизации Win32. Мое первоначальное подозрение заключается в том, что «блокировка» будет более тяжелой в использовании ресурсов, чем событие (и, вероятно, построено поверх события).

+0

Я уверен, что блокировка никак не реализована событием под капотом. IIRC существуют специальные структуры нитей, которые используются специально для управления фиксацией резьбы и делают это более легким, чем события. – sweetlilmre

+0

@sweetlilmre - Я смотрел в реализации Rotor/SharedCCI, и они используют некоторые блокировки в стиле спин-блокировки, но при необходимости они приходят к типу события, предоставленному хостом. –

+0

Да, они вращаются, используя свои собственные биты синхронизации. Единственное, что я не сделал, это то, как они позволяют планировщику Windows знать. Получил код loc? –

1

Один из способов оптимизации работы AutoResetEvent - сохранить свое состояние (сигнализированное/несигнальное) в вашей собственной переменной, поэтому перед тем, как совершить поездку в ядро ​​и на самом деле использовать объект события, вы можете просто проверить состояние вашего приложения переменной и оставаться в режиме пользователя все время.
Я опубликовал demonstration этой концепции пару месяцев назад.

0

Обычно любая синхронизация выполняется медленно, поэтому лучше избегать их.

Однако, есть большая разница между их скорости:

Процессор поддерживается Interlocked Exchange является самым быстрым в то время как простой логический флаг все еще бьется AutoResetEvent.

Проверить это для полных образцов коды и performance comparisons on AutoResetEvent and alternatives.

Смежные вопросы