C# WinFormsでのスレッド間コントロールアクセス

C#では、コントロールへのスレッド間直接アクセスが禁止されています。InvokeRequiredはこの問題を解決するために生まれたものです。あるコントロールのInvokeRequiredプロパティがtrueの場合、そのコントロールを作成したスレッド以外のスレッドからアクセスが試みられていることを意味します。この場合、内部でnew MethodInvoker(LoadGlobalImage)を呼び出して次のステップを実行します。このアプローチはコントロールの安全性を保証します。これを「誰かがあなたにお金を借りたい場合、直接あなたの財布からお金を持っていくのは危険です。そのため、まず相手に伝え、あなた自身が財布からお金を出して貸す」と理解すると分かりやすいでしょう。

設計においてUIとロジックを分離するために、イベントを使用するのが一般的です。UIはイベントに応答して表示処理を行うだけです。しかし、イベントはロジック処理中に異なるスレッドから発生する可能性があり、これらのイベントの応答メソッドがUIのコントロールの内容を変更しようとすると例外が発生します。

ここでControl.InvokeRequiredプロパティとInvokeメソッドが使用されます。

呼び出し元がコントロールのメソッドを呼び出す際に、Invokeメソッドを呼び出す必要があるかどうかを示す値を取得します。呼び出し元がコントロールを作成したスレッド以外のスレッド上にある場合(Invokeメソッドを通じてコントロールを呼び出す必要がある場合)、trueを返します。それ以外の場合はfalseを返します。

Windowsフォームのコントロールは特定のスレッドにバインドされており、スレッドセーフではありません。したがって、**別のスレッドからコントロールのメソッドを呼び出す場合、コントロールのInvokeメソッドの1つを使用して呼び出しを適切なスレードにマーシャリングする必要があります。**このプロパティを使用すると、Invokeメソッドを呼び出す必要があるかどうかを判断できます。どのスレッドがコントロールを所有しているか分からない場合に特に役立ちます。

private delegate void UpdateControlCallback(string message);
void HandleMessageReceived(string message)
{
    if (messageDisplay.InvokeRequired)
    {
        UpdateControlCallback callback = new UpdateControlCallback(HandleMessageReceived);
        messageDisplay.Invoke(callback, new object[] { message });
    }
    else
    {
        messageDisplay.Text = message;
    }
}

説明:この関数はイベントハンドラであり、messageDisplayはテキストボックスです。

これにより、フォーム内のコントロールのスレッド安全性が確保されます。

InvokeRequiredは、現在のスレッドがコントロールを作成したスレッドではない場合にtrueを返します。

例えば、自分でThreadを開始したり、Timerのイベントを使用してフォーム上のコントロールにアクセスしたりする場合、スレッド内でフォームのこのプロパティはTrueになります。

簡単に言えば、Thread AとThread Bの2つのスレッドがあり、Control cがThread A内でnewされた場合、Thread A内で実行されるメソッドのいずれかでc.InvokeRequiredを呼び出してもfalseが返されます。

逆に、Thread B内で実行されるメソッドのいずれかでc.InvokeRequiredを呼び出すとtrueが返されます。

UIスレッドであるかどうかと結果は関係ありません。(通常、ControlはUIスレッドにありますが、例外はあります)

new Control()が呼び出されたときに、コントロールが変数を使用して現在のスレッドを記録し、InvokeRequiredが呼び出されたときに、現在のスレッドがnewしたときに記録されたスレッドと等しくないかどうかを返すと考えることもできます。

タグ: C# WinForms スレッドセーフ Invoke コントロールアクセス

6月30日 16:43 投稿