如何最好地处置由于AutoResetEvent.WaitOne()而正在休眠的线程

时间:2013-02-04 10:45:15

标签: c# multithreading dispose 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 个答案:

答案 0 :(得分:2)

你正在玩一个非常危险的游戏。您的代码特别容易出现死锁。您将看到线程状态为ThreadState.Running,并且线程稍后会在几微秒内调用WaitOne()。你的Join()调用会死锁,永远不会返回。

您可以通过释放AutoResetEvent来获取在WaitOne()调用中被阻止的线程以取消阻止。这将抛出一个可预测的异常,ObjectDisposedException,你可以捕获。使用另一个ManualResetEvent来通知线程退出。这样就不需要Thread.Abort()了。

答案 1 :(得分:1)

请改用BlockingCollection。它将生成简单干净和简短的代码,可以理解,管理和调试......

一个生产者五个消费者......线程101。

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