Как лучше всего расположить спящий поток из-за AutoResetEvent.WaitOne()

У меня есть служба Windows, которая отправляет электронную почту в одном из 5 потоков (сделано для увеличения скорости, с которой служба может отправлять электронную почту):

private AutoResetEvent block;
private ThreadedQueue<Message> messageQueue;        

private void DoSend()
{
    try
    {   
        while(!this.disposing)
        {
            this.block.WaitOne();

            Message message = null; 
            if (this.messageQueue.TryDequeue(out message))
            {                       
                this.block.Set();
            }                   

            if(message != null)
            {
                this.Send(message);                 
            }
        }
    }
    catch(Exception ex)
    {
        // Log
    }
}

у меня есть Queue метод, который добавляет одно или несколько новых сообщений в messageQueue и вызывает block.Set() так что один из 5 потоков может отправить сообщение. Когда одному из потоков разрешено работать, пока в очереди есть сообщения, block.Set() вызывается так, что следующее сообщение может быть выведено из очереди, и другой из 5 потоков будет работать для его отправки. И так до тех пор, пока очередь не опустеет. Это все работает хорошо.

Однако, когда я удаляю свой объект, я устанавливаю переменную распоряжения, а затем для каждого потока:

if(thread.ThreadState == ThreadState.Running)
{
    thread.Join();
}
else if(thread.ThreadState == ThreadState.WaitSleepJoin)
{
    thread.Abort();
}

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

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

ОБНОВИТЬ:

Я изменил выше, чтобы:

private ManualResetEvent block;
private ThreadedQueue<Message> messageQueue;        

private void DoSend()
{
    try
    {   
        while(!this.disposing)
        {
            this.block.WaitOne();

            Message message = null; 
            if (!this.messageQueue.TryDequeue(out message) && !this.disposing)
            {                       
                // There's nothing else to send for now to block the sending threads
                // unless we're disposing as we want the other threads to exit too
                this.block.Reset();
            }                   

            if(message != null)
            {
                this.Send(message);                 
            }
        }
    }
    catch(Exception ex)
    {
        // Log
    }
}

public void Dispose()
{           
    this.disposing = true;
    this.block.Set();           
    foreach(Thread thread in this.sendingThreads) {             
        thread.Join();
    }
    this.block.Dispose();
    this.sendingThreads = null;
}

Спасибо за помощь.

2 ответа

Решение

Вы играете в очень опасную игру. Ваш код особенно подвержен тупику. Вы увидите состояние потока как ThreadState.Running, и поток вызывает функцию WaitOne() через микросекунду. Ваш вызов Join() заблокируется и никогда не вернется.

Вы можете получить поток, который заблокирован при вызове WaitOne(), чтобы разблокировать его, удалив AutoResetEvent. Это создаст предсказуемое исключение ObjectDisposedException, которое вы можете поймать. Используйте другое ManualResetEvent, чтобы сигнализировать потоку о выходе. Нет необходимости в Thread.Abort() таким образом.

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

один производитель пять потребителей... нарезка резьбы 101.

http://msdn.microsoft.com/en-us/library/dd267312.aspx

Другие вопросы по тегам