跨线程冲突

本文关键字:冲突 线程 | 更新日期: 2023-09-27 18:34:39

我有一个带有事件的类和一个自定义的事件参数。有意义的代码:

    public void OnTickReceived(TickReceivedEventArgs e)
    {
        EventHandler<TickReceivedEventArgs> handler = TickReceived;
        if (handler != null)
            handler(this, e);
    }
    public event EventHandler<TickReceivedEventArgs> TickReceived = delegate { };

并在 UI Windows 窗体中使用类,像这样订阅事件

    private void button4_Click(object sender, EventArgs e)
    {
        bool esito;
        t = new T3OpenStockReader();
        esito = t.Connect();
        textBox1.Text += "Connection: " + esito.ToString() + "'r'n";
        Application.DoEvents();
        if (esito)
        {
            esito = t.Subscribe("MI.EQCON.2552");
            textBox1.Text += "Subscription: " + esito.ToString() + "'r'n";
            Application.DoEvents();
        }
        if (esito)
        {
            t.Start();
            t.TickReceived += NewTick_Event;
            System.Diagnostics.Debug.Print("Reading started...");
        }
    }
    private void NewTick_Event(object sender, TickReceivedEventArgs e)
    {
        textBox1.Text += e.tick.DT + " " + e.tick.Price + " " + e.tick.Volume + "'r'n"; 
    }

我收到一个InvalidOperationException - 跨线程操作。我做错了什么?

跨线程冲突

我收到无效操作异常 - 跨线程操作。我的错误在哪里?

据推测T3OpenStockReader在自己的线程上引发事件 - 但您正在尝试修改事件处理程序中的 UI......这意味着你在错误的线程中执行此操作。您可能应该将事件处理程序更改为如下所示的内容:

private void NewTick_Event(object sender, TickReceivedEventArgs e)
{
    Action action = () => textBox1.Text += e.tick.DT + " " + e.tick.Price 
                                           + " " + e.tick.Volume + "'r'n"; 
    textBox1.BeginInvoke(action);
}

我还建议您摆脱Application.DoEvents()调用 - 它们是尝试在 UI 线程中执行太多操作的症状。

我假设您正在尝试更新非 UI 线程上的 UI 组件,即 NewTick_Event .您需要强制更新回 UI 线程,例如

private void NewTick_Event(object sender, TickReceivedEventArgs e)
{
    textBox1.Invoke(new Action(() => textBox1.Text += e.tick.DT + " " + e.tick.Price + " " + e.tick.Volume + "'r'n")); 
}
NewTick_Event 是从

另一个线程调用的,并且必须在 UI 线程上调用控件上的更改(例如,通过使用 BeginInvoke 方法(

private void NewTick_Event(object sender, TickReceivedEventArgs e)
{
   this.BeginInvoke( (Action) () =>
   {
      textBox1.Text += e.tick.DT + " " + e.tick.Price + " " + e.tick.Volume + "'r'n"; 
   });
}

您收到冲突是因为textBox1由不同的线程拥有。您必须在另一个线程上invoke该功能。调用就像向线程(拥有textBox1(询问:"在这里线程,有时间时执行它......"。因此,拥有textBox1的线程将执行该功能,而不是从引发事件的线程(或调用,因为事件是回调......

我会选择这个解决方案。这是一种比调用文本框本身更通用的方法。而且事件的作用并不重要,因此您不必将事件完成的所有功能放在(Action)BeginInvoke中。您只需从拥有文本框的线程再次调用该事件,调用即可。

本主题中的其他答案不检查是否需要调用。我想说,布尔值存在InvokeRequired是有原因的。

下面的示例中的FormParentOfTextBox是放置文本框的窗体的实例。您也可以在此处使用textBox1,但同样,它将变得不那么通用的方法。

    private void NewTick_Event(object sender, TickReceivedEventArgs e)
    {
        //Thread safe approach, generally for every event.
        if (FormParentOfTextBox.InvokeRequired)
        {
            this.Invoke(NewTick_Event(sender, e));
            return;
        }
        textBox1.Text += e.tick.DT + " " + e.tick.Price + " " + e.tick.Volume + "'r'n";
    }