【问题标题】:C#: Events & Thread Safe GUI UpdatesC#:事件和线程安全的 GUI 更新
【发布时间】:2012-02-29 18:00:42
【问题描述】:

我正在编写一个通过 GPIB 命令与多个设备通信的应用程序,并在某些设备上运行测试。我设置了一个类TestProcedure,它将启动一个新线程并运行测试。在整个测试过程中,我设置了几个自定义事件以将信息发送回 GUI。下面是一个简单事件的例子:

    public event InformationEventHandler<string> TestInfoEvent;
    /// <summary>
    /// Event raised when information should be passed back to the main testing form.
    /// </summary>
    /// <param name="s">Information to send back to form.</param>
    private void OnInfo(string s)
    {
        if (TestInfoEvent != null)
            TestInfoEvent(this, s);
    }

这将通过 GUI 处理,更新如下文本框:

TheTestProcedure.TestInfoEvent += new TestProcedure.InformationEventHandler<string> 
                                 (InfoOccurred);
....
private void InfoOccurred(Object sender, string s)
{
    this.textBox1.Text = s + Environment.NewLine + this.textBox1.Text;
    if (this.textBox1.Text.Length > 10000)
        this.textBox1.Text = this.textBox1.Text.Remove(1000);
}

此事件处理似乎工作正常。我没有收到任何跨线程问题,总体上它按预期工作。但是,在另一个表单上,我刚刚添加了一个类似的事件处理程序,它会引发跨线程异常。该事件触发,发送一个简单的类,其中包含我在 InputTextBox(自定义 ComponentOne 控件)中显示的一些信息。特定控件没有 .Invoke 方法,因此我正在寻找异步访问它的替代解决方案。

所以我的问题是,事件处理程序可以安全地访问表单上的控件吗?如果不是,事件处理程序如何触发,有人可以帮助教育我,或提供一些链接信息,关于事件处理程序如何与表单控件通信?我需要锁定事件吗?

【问题讨论】:

    标签: c# .net .net-3.5 event-handling


    【解决方案1】:

    UI 线程上的控件只能从 UI 线程访问 - 来自其他线程的任何访问都必然会导致问题。如果事件不存在,您需要使用 InvokeRequiredBeginInvoke() 将事件编组到正确的线程。

    Example

    【讨论】:

    • 我应该向事件本身添加一个调用吗?这会像调用文本框一样工作吗?编辑:对不起,刚刚看到你的例子我会看看
    • 您首先在事件处理函数中检查InvokeRequired,如果它返回true,您需要使用BeginInvoke() 编组对UI 线程的调用。如果返回值为false,则您已经在UI线程上,因此您可以安全地直接访问控件(例如txtName.Text = ...;是有效的。如果您在错误的线程上,它可能会失败。)
    • 我将答案固定为 BeginInvoke(),因为您可能需要它,而不是 Control.Invoke()
    • 我刚刚测试过,效果很好。感谢您的快速回复!
    【解决方案2】:

    您需要创建一个委托回调并在测试InvokeRequired 属性后执行Invoke()。以下代码将以线程安全的方式处理添加。

    TheTestProcedure.TestInfoEvent += new TestProcedure.InformationEventHandler<string> 
                                 (InfoOccurred);
    
    private void InfoOccurred(Object sender, string s)
    {
        LogMessage(s);
    }
    
    delegate void LogMessageCallback(string text);
    
    void LogMessage(String message)
    {
        if (this.textBox1.InvokeRequired)
            this.Invoke(new LogMessageCallback(LogMessage), message);
        else
        {
            this.textBox1.Text = s + Environment.NewLine + this.textBox1.Text;
            if (this.textBox1.Text.Length > 10000)
                this.textBox1.Text = this.textBox1.Text.Remove(1000);
        }
    }
    

    【讨论】:

    • 什么是“日志”?应该是this.InvokeRequired
    猜你喜欢
    • 2022-01-14
    • 2010-11-05
    • 1970-01-01
    • 2011-04-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-07-12
    • 1970-01-01
    相关资源
    最近更新 更多