Windowsフォームと並列スレッドからの呼び出し

古いフォームを作り直すとき、私は面白い問題に遭遇しました。



このタスクは古典的なものです。バックグラウンドプロセスで何が起こっているかに関する情報をユーザーに表示します。



複雑なことは何もないように思えます。 メインフォームで、ストリームを開始し、その中のデータを処理し、新しいステータスを受信すると、ベースUIスレッド(Invoke / BeginInvoke呼び出し)と同期して、フォームに更新をドロップします。



そして、バックグラウンドスレッドがさらに1つまたは2つを作成しようとする瞬間まで、すべてがうまくいきます。それにより、タスクの一部として追加の作業が委任されます。 跳躍が始まるのはこれらの新しいフローです...



したがって、最初のスレッドが更新用のテキストを受信すると、このテキストがフォームに表示されます。







private delegate void EditStatusTextDelegate(string strArg); private void SendNotificationToForm( string actionText ) { if (this.InvokeRequired) { this.Invoke(new EditStatusTextDelegate(UpdateUI), new object[] { actionText }); return; } UpdateUI(actionText); } private void UpdateUI(actionText) { this.LabelInfo.Text = actionText; }
      
      







または



 private void SendNotificationToForm( string actionText ) { this.BeginInvoke((Action)(() => { this.LabelInfo.Text = actionText; })); }
      
      







1つのスレッドでこれが機能します-メッセージが到着し、フォームがそれらを受信し、データが更新されます。 さらにいくつかを追加すると、SendNotificationToForm関数は予期したとおりに動作しなくなります。 メッセージが到着すると、フォームはそれらを受信します。コンテンツを更新するためにキューに入れます。バックグラウンドスレッドの動作が完了するまで、彼は画面に表示するのを急いでいません。 また、他のフローで実行しているタスクが複雑になるほど、これがより明確になります。 さらに、実際のコードでは、おそらく1つのラベルを変更するだけでなく、視覚化を変更するためのデータセットがはるかに複雑になる可能性があります。







このトピックに関する公式ドキュメントは、InfokeRequiredとBeginInvokeについて頑固に語っています。 しかし実際には、この場合、「通常の方法」を放棄して、同期コンテキストの手動制御に進む必要があります。 データがフォームに到着した時点で更新を行う必要があるため、「手動同期」を実行する必要があります。



これを行うには、フォームを作成した後(コンテキストが既に作成および初期化されている場合)、それを覚えておいてください:



 Fields: private readonly SynchronizationContext syncContext; Constructor: syncContext = SynchronizationContext.Current;
      
      







そして今、別のスレッドからのコールバックで、次のコマンドを実行します。



 private void SendNotificationToForm( string actionText ) { syncContext.Post( UpdateUI, actionText); } private void UpdateUI(actionText) { this.LabelInfo.Text = actionText; }
      
      







この場合、フォームのデータを強制的に同期させ、メインUIスレッドに受信した情報を処理させました。



問題の観点から見ると、解決策は最も簡単ですが、効果的です。 ただし、最初のデータの一部だけでなく、作業ストリームのすべての情報がフォーム上に表示されるようになります。



All Articles