C#:事件和;线程安全GUI更新

C#:事件和;线程安全GUI更新,c#,.net,.net-3.5,event-handling,C#,.net,.net 3.5,Event Handling,我正在编写一个应用程序,通过GPIB命令与多个设备通信,并在一些设备上运行测试。我已经建立了一个类TestProcedure,它将启动一个新线程并运行测试。在整个测试过程中,我设置了几个自定义事件以将信息发送回GUI。以下是一个简单事件的示例: public event InformationEventHandler<string> TestInfoEvent; /// <summary> /// Event raised when informa

我正在编写一个应用程序,通过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);
    }
public event InformationEventHandler testinfo事件;
/// 
///当信息应传回主测试表单时引发的事件。
/// 
///要发送回表单的信息。
私有void OnInfo(字符串s)
{
if(testInfo事件!=null)
TestInfo事件(本,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);
}
TheTestProcedure.TestInfo事件+=新的TestProcedure.InformationEventHandler
(发生的情况);
....
发生私有无效信息(对象发送方,字符串s)
{
this.textBox1.Text=s+Environment.NewLine+this.textBox1.Text;
如果(this.textBox1.Text.Length>10000)
this.textBox1.Text=this.textBox1.Text.Remove(1000);
}
此事件处理似乎工作正常。我还没有收到任何跨线程问题,总的来说,它一直在按预期工作。然而,在另一个表单上,我刚刚添加了一个类似的事件处理程序,它抛出了一个跨线程异常。事件激发,发送一个简单的类,其中包含一些我在InputExtBox(自定义ComponentOne控件)中显示的信息。特定控件没有.Invoke方法,因此我正在寻找异步访问它的替代解决方案


所以我的问题是,事件处理程序访问表单上的控件是否安全?如果没有,事件处理程序是如何启动的,是否有人可以帮助我了解事件处理程序如何与表单控件通信,或者提供一些链接信息?是否需要锁定事件?

只能从UI线程访问UI线程上的控件-任何来自其他线程的访问都会导致问题。您需要使用
invokererequired
BeginInvoke()
将事件封送到正确的线程(如果该线程尚未存在)


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

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);
    }
}
TheTestProcedure.TestInfo事件+=新的TestProcedure.InformationEventHandler
(发生的情况);
发生私有无效信息(对象发送方,字符串s)
{
日志信息;
}
委托无效LogMessageCallback(字符串文本);
无效日志消息(字符串消息)
{
if(this.textBox1.invokererequired)
调用(新LogMessageCallback(LogMessage),message);
其他的
{
this.textBox1.Text=s+Environment.NewLine+this.textBox1.Text;
如果(this.textBox1.Text.Length>10000)
this.textBox1.Text=this.textBox1.Text.Remove(1000);
}
}

我应该向事件本身添加一个调用吗?这会像调用文本框一样工作吗?编辑:抱歉,刚才看到了您的示例,我来看看您首先在事件处理程序函数中检查
invokererequired
,如果它返回
true
,您需要使用
BeginInvoke()
封送对UI线程的调用。如果返回值为
false
您已经在UI线程上,因此您可以安全地直接访问控件(例如
txtName.Text=…;
那么是有效的。如果您在错误的线程上,它可能会失败。)我修复了引用
BeginInvoke()
的答案,因为您可能需要它,而不是
control.Invoke()
。我刚刚测试过,它工作得很好。谢谢你的快速回复!什么是“日志”?它应该是
this.invokererequired