Использование AutoResetEvent для сигнализации рабочего потока - PullRequest
1 голос
/ 06 мая 2011

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

Мой вопрос связан с EventLoop, если возможно, что currentRequest после WaitOne будет нулевым?

Это плохая практика дляесть _eventAvailable.Set () вне блокировки (_eventLocker)?Я переместил это так, чтобы это не начало идти в WaitOne и немедленно оспаривать блокировку (_eventLocker).

Есть предложения, как лучше написать следующий код?

public sealed class RealtimeRunner : MarshalByRefObject
{
    /// <summary>
    /// The actual event, new events get merged into this if it is not null
    /// </summary>
    private Request _pendingRequest;

    /// <summary>
    /// Used to signal the runner thread when an event is available to process
    /// </summary>
    private readonly AutoResetEvent _eventAvailable = new AutoResetEvent(false);
    private readonly object _eventLocker = new object();


    /// <summary>
    /// Called on a background thread via messaging
    /// </summary>
    public void QueueEvent(RealtimeProcessorMessage newRequest)
    {
        bool mergedRequest;
        lock (_eventLocker)
        {
            if (_pendingRequest == null)
            {
                mergedRequest = false;
                _pendingRequest = new Request(newRequest, _engine);
            }
            else
            {
                mergedRequest = true;
                _pendingRequest.Merge(newRequest, _engine);
            }
        }

        _eventAvailable.Set();
    }


    /// <summary>
    /// This is running on its own thread
    /// </summary>
    private void EventLoop()
    {
        while (true)
        {
            // Block until something exists in _pendingRequest
            _eventAvailable.WaitOne();

            Request currentRequest;
            lock (_eventLocker)
            {
                currentRequest = _pendingRequest;
                _pendingRequest = null;
            }

            // CAN THIS EVER BE NULL?
            if (currentRequest == null) 
                continue;

            //do stuff with the currentRequest here
        }
    }
}

1 Ответ

1 голос
/ 06 мая 2011

Да, if (currrentRequest == null) может оценить как истинное.Рассмотрим два потока, которые можно назвать _eventAvailable.Set().Один завершает вызов, а другой получает приоритет.Тем временем поток EventLoop просыпается и завершает всю итерацию цикла.Теперь у вас есть ситуация, когда _pendingRequest равен нулю, а WaitHandle все еще ожидает повторной сигнализации.

Я хочу предложить совершенно другое решение проблемы.Похоже, ваш код может быть упрощен с использованием шаблона «производитель-потребитель».Этот шаблон легче всего реализовать с помощью очереди блокировки.Класс BlockingCollection реализует такую ​​очередь.

public sealed class RealtimeRunner : MarshalByRefObject
{
  private BlockingCollection<Request> m_Queue = new BlockingCollection<Request>();

  public void QueueEvent(RealtimeProcessorMessage newRequest)
  {
    m_Queue.Add(new Request(newRequest _engine));
  }

  private void EventLoop()
  {
    while (true)
    {
      // This blocks until an item appears in the queue.
      Request request = m_Queue.Take();
      // Process the request here.
    }
  }
}
...