在后台线程中侦听事件

时间:2013-10-02 11:18:48

标签: c# multithreading events

我想启动一个侦听事件并在后台处理事件的线程。这就是我到目前为止所做的:

private Thread mThread;
private bool mKeepHandlingIt;

private void Init()
{
    mKeepHandlingIt = true;
    mThread = new Thread(ProcessEvents);
    mThread.SetApartmentState(ApartmentState.STA);
    mThread.Start();
}

private void ProcessEvents()
{
    StaticClass.CoolEvent += HandleIt;
    StaticClass.StartDoingStuff();

    while (mKeepHandlingIt)
    {
        Application.DoEvents();
    }

    StaticClass.StopDoingStuff();
    StaticClass.CoolEvent -= HandleIt;
}

private void HandleIt(object sender, EventArgs e)
{
    //Handles it
}

protected override void Dispose()
{
    if (mThread != null)
    {
        mKeepHandlingIt = false;
        mThread.Join();
    }
}

有更好的方法吗?就像一个更适合这个目的的线程类?在这种情况下,BackgroundWorker似乎不是更好的选择...

如果这是一个很好的方法,那么我有一个我无法理解的问题。 上面的解决方案适用于侦听事件和处理它们,但是当调用Dispose并且mKeepHandlingIt设置为false时,退出while循环(如果我在循环中放置断点,调试器不会中断)但是后面没有代码执行循环并且mThread.Join永远不会返回。基本上......我想知道的是:如何停止线程,然后确保在清理之前不要继续?

2 个答案:

答案 0 :(得分:6)

此代码有许多错误。首先,它遭受了事件处理程序在该工作线程上运行的错觉。这不是事件的工作方式,处理程序在与触发事件的代码完全相同的线程上运行。 .NET中没有机制让它跳转到另一个线程。

使用调试器的Debug + Windows + Threads窗口可以看到的东西。在Init()方法上设置一个断点,在事件处理程序上设置一个断点。当它们中断时,切换到该调试器屏幕并记下所选的线程。

使用Application.DoEvents()非常危险。但不是这里,线程没有创建任何窗口,所以没有事件要做。相反,该线程将只消耗100%的核心,而根本不会完成任何事情。

mKeepHandlingIt变量通常不会按照您的希望执行。当您在32位计算机上运行代码的Release版本时,线程将从不看到它被设置为false,因此线程将永远不会退出。而Thread.Join()将陷入僵局。这是由抖动优化器引起的,它将bool变量存储在cpu寄存器中,并且永远不会从内存中重新加载它。您必须声明变量 volatile 以防止进行此优化。哪个是黑客攻击,你应该总是使用适当的同步对象来发送线程信号,这里适合使用AutoResetEvent。

只需删除此代码,它对您没有帮助。

答案 1 :(得分:2)

对于大多数案例,您绝不应该使用doevents。秒。

例如见:

如果没有更多线程可用,则使用新线程创建线程可能会遇到麻烦。因此,您可以使用Threadpool

但我会 stronlgy 建议您查看TasksTPL

然后你可以从

开始
Task.Factory.StartNew()

当我们使用异步库并且你有像continuewith

这样的函数时,还有等待

另见MSDN await one ore more tasks

            // Wait for all tasks to complete.
            Task[] tasks = new Task[10];
            for (int i = 0; i < 10; i++)
            {
                tasks[i] = Task.Factory.StartNew(() => DoSomeWork(10000000));
            }
            Task.WaitAll(tasks);

Async和await也不一定使用线程但是异步工作,请参阅:async-await-vs-threads

这对安全资源来说是一个很大的好处。特别是当你有io操作时。

另一种更复杂的方法是使用某种服务总线(你想要达到的目标)在单独的过程中实现监听。