【问题标题】:How best to dispose a thread that is sleeping because of AutoResetEvent.WaitOne()如何最好地处理因 AutoResetEvent.WaitOne() 而处于休眠状态的线程
【发布时间】:2013-02-05 09:50:56
【问题描述】:

我有一个 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;
}

感谢您的帮助。

【问题讨论】:

  • 创建一个特殊的“死亡消息”,导致任何接收它的线程终止。然后排队尽可能多的死亡消息,因为你有需要终止的线程。
  • ..或安排任何获得“死亡消息”的线程在终止之前重新排队。这将通过一条消息杀死所有线程。

标签: c# multithreading dispose autoresetevent waitone


【解决方案1】:

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

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

【讨论】:

  • 观察力不错。是的,这是可能的。我认为如果需要删除 ThreadState.Running。与您一起提出解决方案;您是否建议我处理 AutoResetEvent 对象并捕获结果异常。为什么我需要另一个ManualResetEvent,上面的代码一旦遇到ObjectDisposedException肯定会退出?
  • 调用 block.Dispose() 不会导致线程上的块被释放。他们仍然睡着。
  • 仅处理 ARE 是不够的,线程可能没有阻塞它。你需要一个额外的信号来让它看到它需要退出它的处理循环。使用调试器查看它在做什么。调试 + Windows + 线程。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-07-17
    • 1970-01-01
    • 1970-01-01
    • 2020-04-30
    • 2017-05-29
    • 1970-01-01
    • 1970-01-01
    • 2013-11-22
    相关资源
    最近更新 更多