2013-10-08 4 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 { }; 

и потребляя класс в форме пользовательского интерфейса 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 вызывает события на своем собственном потоке - но вы пытаетесь изменить пользовательский интерфейс в обработчике событий ... что означает, что вы делаете это в неправильном потоке. Вы, вероятно, следует изменить обработчик события на что-то вроде:

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() звонков - они симптом пытаются сделать слишком много в потоке пользовательского интерфейса.

1

Я предполагаю, что вы пытаетесь обновить компонент пользовательского интерфейса в потоке, отличном от UI, то есть NewTick_Event. Вам нужно принудительно обновить обновление на потоке пользовательского интерфейса, например.

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 вызывается из другого потока и изменения на органах управления должны вызываться в потоке пользовательского интерфейса (например, с помощью метода 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 Но это тот факт, что этого класса нет. Обработчик события находится в определении формы, а не в определении вашего класса, поэтому он сортирует свой собственный поток пользовательского интерфейса. – Servy

+0

Единственный вариант - сделать поток потоков T3OpenStockReader безопасным, например, передав SynchronizationContext в конструкторе. Вам нужен какой-то код? – Pellared

0

Вы получаете нарушение, потому что textBox1 является собственностью другой поток. У вас должно быть invoke функциональность на другом потоке. Вызов - это как просить поток (которому принадлежит textBox1): «Здесь поток, выполняйте это, когда у вас есть время ...». поэтому поток, которому принадлежит textBox1, будет выполнять функциональность, а не поток, из которого событие возникает из (или называется, поскольку события - это обратные вызовы ...).

Я бы выбрал это решение. Это более общий подход, чем использование самого текстового поля. И не имеет значения, что делает это событие, поэтому вам не нужно включать все функциональные возможности этого события в (Action) или BeginInvoke. Вы просто просто вызываете событие снова, вызывается из потока, которому принадлежит текстовое поле.

Другие ответы в этом разделе не проверяют необходимость использования invoke. Я бы сказал, что InvokeRequired boolean есть по какой-то причине.

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"; 
    } 
Смежные вопросы