C#,事件处理程序和线程

8

我正在编写一个小型聊天应用程序,我有以下事件处理程序:

void o_Typing(object sender, EventArgs e)
{
    MessageBox.Show("Fired!");
    this.Text = "Fired!";
}

o_Typing是从TabPage派生的类中的一个方法。基本上,我想让每个对话都有自己的选项卡。

事件处理程序由我的Chat对象触发,该对象在另一个线程中运行。我有1个UI线程和另一个线程用于每个Chat对话(以保持轮询服务器以获取新数据)。

当事件被触发时,MessageBox弹出,但选项卡标题不会更改。在事件触发一次后,它再也不会触发,这使我相信该事件在工作线程中被调用,尽管它是在UI线程中定义的。

如何使我的事件从工作线程中调用,并使用Invoke()在UI线程上执行它们?

2个回答

10

有两种选择:

1) 使事件处理程序具备线程安全性:在需要与UI线程通信的任何事件处理程序中使用Control.Invoke/BeginInvoke

2) 在引发事件之前,使工作线程回到UI线程 - 换句话说,在引发事件的过程中使用Control.Invoke,以便所有事件处理程序都在UI线程中调用。根据你的应用程序结构,你可能不希望你的事件引发组件明确了解UI - 但是在构造时,可以传递一个ISynchronizeInvoke(由Control实现),你的组件可以使用它来在正确的线程上引发其事件。当然,这仅在每个事件处理程序都愿意在相同线程上运行时才有效 - 但这通常是成立的。你可以编写如下代码:

protected void OnFoo(EventArgs args)
{
    if (sync != null && sync.InvokeRequired)
    {
        sync.Invoke((Action) delegate { OnFoo(args) }, null);
        return;
    }
    EventHandler handler = Foo; // Where Foo is the event name
    if (handler != null)
    {
        handler (this, args);
    }
}

谢谢,我在事件处理程序上使用了Invoke(),它非常好用。 - user47322

5
如果您在由工作线程执行的代码中触发事件,那么所有订阅该事件的方法将在该工作线程下执行。对于GUI元素,您需要查看Invoke方法。此致最好的问候。

你不需要这样做。你可以在工作线程上调用事件,每个订阅者都应该检查它是否在正确的线程上,并在需要时调用自己。 - Matthew Scharley
或者,显然您可以像Jon建议的那样去做。 - Matthew Scharley
两个答案都不错,但Jon Skeet的回答更加直接。无论如何,还是非常感谢Oliver。 - user47322

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接