需要有关事件处理和代理的说明

时间:2011-02-23 06:40:40

标签: c# .net multithreading delegates invoke

我有一些我写的代码,它可以满足我的需求。但是,我不太确定它是如何工作的。我最麻烦的部分是最后一部分。我有一个textBox1.Text =“test”,它没有用。关于从不同的线程调用它的运行时错误。当我把textBox1.Invoke(等等),它按预期工作。为什么?

正如你所看到的,我知道这很危险,我真的很想知道这里发生了什么,而不是盲目地从网站上复制和粘贴。

我在名为SerialCommunicator的类中有以下内容:

public SerialCommunicator(SerialPort sp)
{
    this.sp = sp;
    sp.ReceivedBytesThreshold = packetSize;
    sp.DataReceived += new SerialDataReceivedEventHandler(sp_DataReceived);
    sp.Open();
}    
public void sp_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    Thread.Sleep(50);
    SerialPort s = (SerialPort)sender;
    byte[] buffer = new byte[128];
    s.Read(buffer, 0, s.BytesToRead);
}

然后,在我的Form1.cs中,我有一个按钮,按下时会执行以下操作:

private void btnPortOK_Click(object sender, EventArgs e)
{
    string comPort = cboComPorts.SelectedItem.ToString();
    SerialPort sp = new SerialPort(comPort, 9600, Parity.None, 8, StopBits.One);
    sp.DataReceived += new SerialDataReceivedEventHandler(DataHasBeenReceived);
    comm = new SerialCommunicator(sp);
}
public void DataHasBeenReceived(object sender, EventArgs args)
{
    textBox1.Invoke(new EventHandler(delegate { textBox1.Text += "test"; }));
}

5 个答案:

答案 0 :(得分:4)

这是线程亲和力。除了创建它们的线程之外,UI控件不喜欢被任何东西触及,但是DataReceived线程来自不同的线程。添加对Control.Invoke的调用将工作项推送回UI线程,因此更新的Text可以成功。

答案 1 :(得分:2)

我不是这方面的专家(可能会有更好的答案)。但据我了解,GUI线程“拥有”您的表单。因此,当您尝试从不同的线程更新它时,您正在跨越流。

Invoke是一种让GUI线程运行方法的方法。它运行的方法是textBox1.Text += "test";

这个想法是通过调用一个委托,它将要求GUI线程进行更改,而不是仅仅自己更改值。这允许允许以线程安全的方式进行更改。

这是Jon Skeet关于这个问题的好文章: http://www.yoda.arachsys.com/csharp/threads/winforms.shtml

答案 2 :(得分:1)

从发生它们的线程调用事件。 (除非另有说明)。

想一想:
当您激活该事件时,它实际上被称为finction EventName()。所以调用一个事件意味着实际上去了那个注册到该事件并执行它们的所有方法 但是,这是以串行方式在同一个线程中完成的。

因此,如果某个事件发生在不是您的UI线程的线程中,您将收到错误。

答案 3 :(得分:1)

textBox1.Text = "test"不起作用,因为您是从另一个线程(即DataHasBeenReceived事件)调用它,然后是拥有该文本框的线程。这通常是应用程序运行的线程,它创建了GUI界面(从而创建了文本框)。 Invoke有效,因为该方法切换到GUI线程,设置文本然后切换回DataHasBeenReceived事件的主题。

在Net 1.0和1.1中,您可以使用来自另一个线程的GUI控件,然后是拥有它们的GUI控件,但是当线程同时开始访问控件时,这会导致很多问题。所以,自从2.0版微软改变了这一点。

如果您想知道是否必须使用invoke(即,如果可以从GUI线程或其他线程调用方法),则可以使用属性InvokeRequired和if else。调用调用比直接操作控件稍贵一些。

答案 4 :(得分:1)

问题是GUI组件只接受来自GUI线程的修改。因此,当其他线程想要修改GUI时,他们必须使用诸如control.Invoke(...)之类的措施对其修改代码进行排队,这些措施将在GUI事件队列中尽快处理委托,从而对正确的线程进行处理。 / p>

你遇到的是,其中一个内置检查被触发而不是调用线程确实是正确的线程的控件。这是一个安全措施,使调试更容易(如果它们不存在,你将不得不调试细微的线程问题......)