AutoResetEvent процесс? - PullRequest
       25

AutoResetEvent процесс?

1 голос
/ 19 июля 2011
private ConcurrentQueue<Data> _queue = new ConcurrentQueue<Data>();
private AutoResetEvent _queueNotifier = new AutoResetEvent(false);

public void MoreData(Data example)
{
    _queue.Enqueue(example);
    _queueNotifier.Set();
}

private void _SimpleThreadWorker()
{
    while (_socket.Connected)
    {
        _queueNotifier.WaitOne();
        Data data;
        if (_queue.TryDequeue(out data))
        {
            //handle the data
        }
    }
}

Должен ли я установить для события значение false, как только оно будет удалено, или событие само по себе возвращается к значению false, когда оно возвращает _queueNotifier.WaitOne() или как оно работает точно?

Должен ли я использовать внутреннее время, как показано в примере ниже, или оба пути просто хороши / равны?

while (_socket.Connected)
{
    _queueNotifier.WaitOne();
    while (!_queue.IsEmpty)
    {
        Data data;
        if (_queue.TryDequeue(out data))
        {
            //handle the data
        }
    }
}

Ответы [ 2 ]

4 голосов
/ 19 июля 2011

Если вы используете ConcurrentQueue из .NET 4, лучше избегать полной обработки AutoResetEvent.Вместо этого создайте BlockingCollection, чтобы обернуть ConcurrentQueue, и просто используйте его - он делает все, что вам нужно.(Если вы просто создадите BlockingCollection с помощью конструктора без параметров, он все равно создаст для вас ConcurrentQueue.)

РЕДАКТИРОВАТЬ: если вы действительно хотите использовать AutoResetEvent, тогда WaitOneавтоматически (и атомарно) сбросит событие - это часть "Авто" в AutoResetEvent.Сравните это с ManualResetEvent, который не сбрасывает событие.

1 голос
/ 19 июля 2011

Когда вы делаете _queueNotifier.Set() событие становится сигнальным. Когда событие сигнализируется и _queueNotifier.WaitOne() вызывается из другого потока, две вещи происходят одновременно (т.е. в режиме ядра):

  • Событие становится неподписанным (так как оно автоматически сбрасывается)
  • Поток, вызывающий WaitOne, разблокирован

Так что вам не нужно явно устанавливать статус события самостоятельно.

Однако, как говорит Джон, если единственное, что вы делаете с вашими общими переменными, - это выталкивание и извлечение элементов из очереди, просто использование BlockingCollection более удобно.

Если вы обращаетесь к нескольким общим переменным, возможно, имеет смысл использовать механизм синхронизации одного потока (ваш собственный).

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

public void MoreData(Data example)
{
    var queueWasEmpty = _queue.IsEmpty;
    _queue.Enqueue(example);
    if (queueWasEmpty) {
        _queueNotifier.Set();
    }
}

private void _SimpleThreadWorker()
{
    while (_socket.Connected)
    {
        _queueNotifier.WaitOne();
        Data data;
        while(!queue.IsEmpty) {
            if (_queue.TryDequeue(out data))
            {
                //handle the data
            }
        }
    }
}

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

Это правда, что в отношении последнего избегание переключения контекста происходит за счет добавления теста _queue.IsEmpty (где плохая реализация в любом случае может сделать переключение контекста); тем не менее, этот, вероятно, реализован как операция обмена с блокировкой сравнения, которая не требует перехода в режим ядра.

Отказ от ответственности: Я не проверял исходный код или IL для проверки вышеуказанной информации.

...