マルチスレッドを使用して Windows フォーム アプリケーションのパフォーマンスを向上させる場合は、コントロールがスレッド セーフな方法で呼び出されることを確認する必要があります。
Windows フォーム コントロールへのアクセスは、本質的にスレッド セーフではありません。コントロールの状態を操作するスレッドが 2 つ以上ある場合、コントロールを強制的に不整合な状態にする可能性があります。競合状態やデッドロックなど、他のスレッド関連のバグが発生する可能性があります。必ずスレッドセーフな方法でコントロールにアクセスしてください。
1. 初心者がよく遭遇する問題
コントロールの作成に Invoke メソッドを使用していないスレッドからコントロールを呼び出すのは安全ではありません。以下は非スレッドセーフな呼び出しの例です。実行時に InvalidOperationException メッセージがスローされ、「コントロール コントロール名は、コントロールを作成しなかったスレッドからアクセスされています。」というエラーが表示されます。
// This event handler creates a thread that calls a // Windows Forms control in an unsafe way.private void setTextUnsafeBtn_Click( object sender, EventArgs e) { this.demoThread = new Thread(new ThreadStart(this.ThreadProcUnsafe)); this.demoThread.Start(); }// This method is executed on the worker thread and makes// an unsafe call on the TextBox control.private void ThreadProcUnsafe() { this.textBox1.Text = "This text was set unsafely."; }
2. 解決策
Windows フォーム コントロールへのスレッドセーフな呼び出しを行う必要がある場合。
①コントロールのInvokeRequiredプロパティをクエリします。
②InvokeRequiredがtrueを返した場合、実際の呼び出しコントロールのデリゲートを使用してInvokeを呼び出します。
③InvokeRequired が false を返した場合は、コントロールを直接呼び出してください。
ここでは同期実行委任と非同期実行委任に分けられます。
次のコード例では、スレッドセーフな呼び出しが ThreadProcSafe メソッドに実装されており、バックグラウンド スレッドによって実行されます。 TextBox コントロールの InvokeRequired が true を返した場合、ThreadProcSafe メソッドは SetTextCallback インスタンスを作成し、それをフォームの Invoke メソッドに渡します。これにより、TextBox コントロールを作成したスレッドで SetText メソッドが呼び出され、そのスレッドのコンテキストで Text プロパティが直接設定されます。
// This event handler creates a thread that calls a // Windows Forms control in a thread-safe way. private void setTextSafeBtn_Click( object sender, EventArgs e) { this.demoThread = new Thread(new ThreadStart(this.ThreadProcSafe)); this.demoThread.Start(); }// This method is executed on the worker thread and makes // a thread-safe call on the TextBox control. private void ThreadProcSafe() { this.SetText("This text was set safely."); }// This delegate enables asynchronous calls for setting // the text property on a TextBox control.delegate void SetTextCallback(string text); // This method demonstrates a pattern for making thread-safe // calls on a Windows Forms control. // // If the calling thread is different from the thread that // created the TextBox control, this method creates a // SetTextCallback and calls itself asynchronously using the// Invoke method. // // If the calling thread is the same as the thread that created // the TextBox control, the Text property is set directly. private void SetText(string text) { // InvokeRequired required compares the thread ID of the // calling thread to the thread ID of the creating thread. // If these threads are different, it returns true. //this.textBox1.InvokeRequired will be replaced by //this.InvokeRequired, if want to set many controls' //attribute or text. if (this.textBox1.InvokeRequired)// or this.InvokeRequired { SetTextCallback d = new SetTextCallback(SetText); this.Invoke(d, new object[] { text }); } else { this.textBox1.Text = text; } }
3.BackgroundWorker コンポーネント
アプリケーションでマルチスレッドを実装するための推奨される方法は、BackgroundWorker コンポーネントを使用することです。 BackgroundWorker コンポーネントは、マルチスレッド処理にイベント駆動型モデルを使用します。バックグラウンド スレッドは DoWork イベント ハンドラーを実行し、コントロールを作成したスレッドは ProgressChanged および RunWorkerCompleted イベント ハンドラーを実行します。 ProgressChanged および RunWorkerCompleted イベント ハンドラーからコントロールを呼び出すことができます。
①バックグラウンドスレッドで実行したい作業を実行するメソッドを作成します。このメソッドでは、メインスレッドから作成されたコントロールを呼び出さないでください。
②バックグラウンド作業終了後にバックグラウンド作業の結果を報告するメソッドを作成します。 メインスレッドによって作成されたコントロールは、このメソッドで呼び出すことができます。
③ 手順1で作成したメソッドをDoWorkインスタンスのBackgroundWorkerイベントにバインドし、手順2で作成したメソッドを同じインスタンスのRunWorkerCompletedイベントにバインドします。
④ バックグラウンド スレッドを開始するには、RunWorkerAsync インスタンスの BackgroundWorker メソッドを呼び出します。
次のコード例では、DoWork イベント ハンドラーは Sleep を使用して、時間のかかる作業をシミュレートします。フォームの TextBox コントロールは呼び出しません。 TextBox コントロールの Text プロパティは、RunWorkerCompleted イベント ハンドラーで直接設定されます。
// This BackgroundWorker is used to demonstrate the // preferred way of performing asynchronous operations.private BackgroundWorker backgroundWorker1; // This event handler starts the form's // BackgroundWorker by calling RunWorkerAsync. // // The Text property of the TextBox control is set // when the BackgroundWorker raises the RunWorkerCompleted // event.private void setTextBackgroundWorkerBtn_Click( object sender, EventArgs e) { this.backgroundWorker1.RunWorkerAsync(); }// This event handler sets the Text property of the TextBox // control. It is called on the thread that created the // TextBox control, so the call is thread-safe. // // BackgroundWorker is the preferred way to perform asynchronous // operations.private void backgroundWorker1_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) { this.textBox1.Text = "This text was set safely by BackgroundWorker."; }
ProgressChanged イベントを使用して、バックグラウンド タスクの進行状況をレポートすることもできます。このイベントを含む例については、「BackgroundWorker」を参照してください。
上記は、メインスレッド (UI スレッド) コントロールにアクセスする 0 子スレッドからの C#02 の独習の内容です。その他の関連コンテンツについては、PHP 中国語 Web サイト (m.sbmmt.com) に注目してください。