2016-10-20 7 views
1

作業中のプロジェクトでCross-thread operation not validエラーが発生しました。スレッドプロセスでイベントを処理する

Class MyObject 
{ 
    public delegate void MessageHandler(object sender, EventArgs e); 

    public event MessageHandler OnConnect; 

    public void Process() 
    { 
     await Connection();//Line highlighted when exception is raised. 
    } 

    private async Task Connection() 
    { 
     await 'blocking task here' 
     OnConnect(this, new EventArgs()); 
    } 
} 


//Windows form 
MyObject o = new MyObject(); 

o.OnConnect += o_OnConnect; 

Thread connectionThread = new Thread(o.Process); 
connectionThread.Start(); 


void o_OnConnect(object snder, EventArgs e) 
{ 
    listBox.items.add("Connection"); 
} 

基本的な概要です。ここのスレッドプロセスは接続以外の作業を行い、他のビジネスも処理し、生き残る必要があることに注意してください。

すべてがうまくいきます。バックグラウンドで接続を開始し、リストボックスアイテムを追加しようとするとスレッドエラーが発生します。

誰でも私のためにこれについていくつかの光を当てることができますか?ハンドラはフォームスレッド上にあり、ここではこのボックスから項目を追加/削除できます。

私はスレッドピースを削除するには

o.Process(); 

にスレッド呼び出しラインをも変更しましたが、私は正確に同じエラーを取得します。

私が何か明白なものを見逃しているのか、私はここの雑草に脱出しているのか分かりません。私は実際にこのスレッドに非同期コードを実行させ、処理のために呼び出された場所にイベントを戻したいだけです。

+0

[C#の別のスレッドからGUIを更新するには?](http://stackoverflow.com/questions/661561/how-to-update-the-gui-from-another-thread-in-c) –

+0

https://msdn.microsoft.com/en-us/library/zyzhdc6b(v=vs.110).aspx |あなたはGUIを更新するためにinvokeを使うことができました –

+0

これは一例にすぎませんが、 'public void Process()'は本当に 'public Task Process()'でなければなりません。イベントハンドラを記述していない限り、 'async void'を実行すべきではありません。 –

答えて

1

誰も私のために、この上でいくつかの光を当てることができますか?ハンドラはフォームスレッド上にあり、ここではこのボックスから項目を追加/削除できます。

いいえ、ハンドラは「フォームスレッド」にありません。フォームはUIスレッドに属し、ハンドラは独自の接続スレッドから実行されます。イベントハンドラコードはオブジェクトの形式ですが、接続スレッドで実行されます。

最高の解決方法は、クロススレッドイベントをまったく使用しないことです。個人的には、代わりにReactive Extensionsを使用します。 new Threadはすでにレガシーコードを持っていることを意味していること

//Windows form 
var ui = SynchronizationContext.Current; 
MyObject o = new MyObject(); 
o.OnConnect += (sender, args) => ui.Post(_ => o_OnConnect(sender, args), null); 

Thread connectionThread = new Thread(o.Process); 
connectionThread.Start(); 

注:反応性の拡張機能を学ぶために喜んでいないのであれば

しかし、あなたはUIスレッドでハンドラを高めるためにSynchronizationContextTaskSchedulerを使用することができます。ほぼ確実に良い解決策があります。

+0

Mr. Cleary、いつもの情報に感謝します。私はあなたのブログを(このプロジェクトでは偶然にも)TCP操作に使っています。 ReactiveXを私のリストに入れました。私はちょうど一緒に素早く一緒に何かをcobbleしようとしていたと思うと、アプローチをすべて間違ってから始める。おそらく 'SynchronizationContext'を使ってプロジェクトを再び動かすでしょう。 – Bmo

0

他のスレッドからUI要素にアクセスすることはできません。メインのGUIスレッドですべてのUIアクセスを行う必要があります。長期間実行されるタスク自体を非同期にするだけで、待ってからGUIが機能します。

+0

他のスレッドの中で 'BeginInvoke'を使うのはどうですか? –

0

"MyObject"のイベントが別のスレッドで発生している場合は、コントロールを操作するためにGUIスレッドと同期する必要があります。以下のコードはこれを手助けするでしょう。

void o_OnConnect(object snder, EventArgs e) 
{ 
     if(listBox.InvokeRequired) 
     { 
      listBox.BeginInvoke((MethodInvoker)delegate () 
      { 
       listBox.items.add("Connection"); 
      }); 
     } 
     else 
     { 
      listBox.items.add("Connection");   
     } 
}