2013-10-08 83 views
1

我有一个具有事件和自定义EventArgs的类。有意义的代码:Cross.Thread违规

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 - cross.thread操作。我究竟做错了什么?

+4

你不应该是,我看你不需要,在那个按钮点击处理程序中调用DoEvents。不必要地打开各种错误/问题。 – Servy

+0

谢谢DoEvents是最后一次代码重构的补充。 –

+0

然后只是认识到它是值得去除的,因为它是有害的,而不是无用的。 – Servy

回答

3

我收到InvalidOperationException - cross.thread操作。我的错误在哪里?

想必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线程的症状。

1

我假设您正在尝试更新非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")); 
} 
0

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"; 
    }); 
} 
+0

感谢您提供所有建议的解决方案,但我正在寻找一种解决方案,不会强迫我知道客户端(UI)T3OpenStockReader类在多线程中工作。该类可以分发给其他开源程序员。 –

+0

@StefanoGardini但这就是关键,那个阶级没有。事件处理程序位于表单的定义中,而不在您的类的定义中,因此它将封送到其自己的UI线程中。 – Servy

+0

唯一的选择是使T3OpenStockReader线程安全,例如通过在构造函数中传递SynchronizationContext。你想要一些代码吗? – Pellared

0

你得到的侵犯,因为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"; 
    }