誰もがこの上に書かれたこの声明を説明してください link
Invoke(Delegate):
コントロールの基になるウィンドウハンドルを所有するスレッドで、指定されたデリゲートを実行します。
誰がこれが何を意味するのか説明できますか(特に大胆なもの)私はそれを明確に理解することができません
この質問に対する答えは、C#コントロールの仕組みにあります。
Windowsフォームのコントロールは特定のスレッドにバインドされており、スレッドセーフではありません。したがって、別のスレッドからコントロールのメソッドを呼び出す場合は、コントロールのinvokeメソッドのいずれかを使用して、呼び出しを適切なスレッドにマーシャリングする必要があります。このプロパティは、invokeメソッドを呼び出す必要があるかどうかを判断するために使用できます。これは、どのスレッドがコントロールを所有しているかわからない場合に役立ちます。
事実上、Invokeが行うことは、呼び出しているコードが、コントロールが「存続している」スレッド上で発生し、クロススレッド例外を効果的に防止することを確実にすることです。
歴史的観点から、.NET 1.1では、これは実際には許されていました。それが意味することはあなたがどんなバックグラウンドスレッドからも "GUI"スレッド上でコードを試みて実行することができて、これが大抵うまくいくということです。他のことをしている間にGUIスレッドを効果的に中断していたために、アプリケーションが終了することがありました。これはクロススレッド例外です - GUIが何か他のものを描画している間にTextBoxを更新しようとすることを想像してください。
事実上、あなたは待ち行列を中断しています。 Invokeは、実際にあなたがやりたいことをそのキューに入れるための「丁寧な」方法であり、この規則は.Net 2.0以降からスローされた InvalidOperationException によって実施されました。
舞台裏で実際に何が起こっているのか、そして「GUI Thread」が何を意味するのかを理解するためには、Message PumpまたはMessage Loopが何であるかを理解することは有用です。
これは実際には既に " メッセージポンプとは "という質問で答えられており、コントロールと対話するときに結びついている実際のメカニズムを理解するために読むことをお勧めします。
あなたが役に立つと思うかもしれない他の読書は以下を含みます:
Windows GUIプログラミングの基本的な規則の1つは、コントロールを作成したスレッドだけがその内容にアクセスおよび/またはその内容を変更できることです(いくつかの文書化された例外を除く)。他のスレッドから実行してみてください。デッドロックから、例外、例外が半分更新されたUIまで、予測不可能な動作が発生します。別のスレッドからコントロールを更新する正しい方法は、適切なメッセージをアプリケーションメッセージキューにポストすることです。メッセージポンプがそのメッセージの実行に慣れると、コントロールはそれを作成したのと同じスレッド上で更新されます(メッセージポンプはメインスレッド上で実行されます)。
そして、代表的なサンプルを使った、よりコード重視の概要については、
// the canonical form (C# consumer)
public delegate void ControlStringConsumer(Control control, string text); // defines a delegate type
public void SetText(Control control, string text) {
if (control.InvokeRequired) {
control.Invoke(new ControlStringConsumer(SetText), new object[]{control, text}); // invoking itself
} else {
control.Text=text; // the "functional part", executing only on the main thread
}
}
InvokeRequiredに感謝したら、これらの呼び出しをラップするために拡張メソッドを使用することを検討することをお勧めします。これはStack Overflowの質問 Invoke Requiredでリタードされたコードのクリーンアップ で十分にカバーされています。
さらに 歴史的に起こったことの書き直し それは興味深いかもしれません。
Windowsフォームのコントロールまたはウィンドウオブジェクトは、handle(HWNDとも呼ばれる)で識別されるWin32ウィンドウを囲む単なるラッパーです。あなたがコントロールでするほとんどのことは結局このハンドルを使うWin32 API呼び出しになるでしょう。ハンドルはそれを作成したスレッド(通常はメインスレッド)によって所有されており、別のスレッドによって操作されるべきではありません。何らかの理由で別のスレッドからの制御で何かをする必要がある場合は、Invoke
を使用してメインスレッドに代わりにそれを実行させることができます。
たとえば、ワーカースレッドからラベルのテキストを変更する場合は、次のようにします。
theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));
コントロールを変更したい場合は、コントロールが作成されたスレッドで行わなければなりません。このInvoke
メソッドを使用すると、関連付けられたスレッド(コントロールの基になるウィンドウハンドルを所有するスレッド)でメソッドを実行できます。
以下のサンプルでは、SetText1が別のスレッドからtextBox1.Textを変更しようとしているため、thread1は例外をスローします。しかしthread2では、SetText2のActionはTextBoxが作成されたスレッドで実行されます。
private void btn_Click(object sender, EvenetArgs e)
{
var thread1 = new Thread(SetText1);
var thread2 = new Thread(SetText2);
thread1.Start();
thread2.Start();
}
private void SetText1()
{
textBox1.Text = "Test";
}
private void SetText2()
{
textBox1.Invoke(new Action(() => textBox1.Text = "Test"));
}
Invoke((MethodInvoker)delegate{ textBox1.Text = "Test"; });
this.Invoke(delegate)
は、メインスレッドまたは作成されたスレッドでデリゲートをthis.Invoke()
への引数に呼び出すことを確認します。
Thumbルールはメインスレッド以外からはフォームコントロールにアクセスしないと言えます。
次の行がInvoke()の使用に意味があるかもしれません
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.
if (this.textBox1.InvokeRequired)
{
SetTextCallback d = new SetTextCallback(SetText);
this.Invoke(d, new object[] { text });
}
else
{
this.textBox1.Text = text;
}
}
メインスレッドで実行されるスレッドプールスレッド(つまりワーカースレッド)を作成しても状況があります。それは新しいスレッドを作成しませんcozメインスレッドはさらなる命令を処理するために利用可能です。そのため、最初に現在のコードがワーカースレッドで実行されているのでtrueを返すthis.InvokeRequired
ifを使用して、現在実行中のスレッドがメインスレッドかどうかを調べます。これを呼び出します。
それ以外の場合は直接UIコントロールを更新します(ここではメインスレッドでコードを実行していることが保証されています)。
実際的には、デリゲートはメインスレッドで呼び出されることが保証されています。これは重要です。Windowsコントロールの場合、メインスレッドでプロパティを更新しないと、変更が表示されないか、コントロールで例外が発生するからです。
パターンは次のとおりです。
void OnEvent(object sender, EventArgs e)
{
if (this.InvokeRequired)
{
this.Invoke(() => this.OnEvent(sender, e);
return;
}
// do stuff (now you know you are on the main thread)
}
つまり、バックグラウンドワーカーまたはスレッドプールスレッドからそのメソッドを呼び出しても、デリゲートはUIスレッド上で実行されます。 UI要素にはthread affinityがあります。それらは1つのスレッド、つまりUIスレッドと直接対話するだけです。 UIスレッドは、として定義されているコントロールインスタンスを作成したスレッドであるため、ウィンドウハンドルに関連付けられています。しかし、それはすべて実装の詳細です。
重要な点は、(ラベルの値を変更するなどのために)UIにアクセスできるように、ワーカースレッドからこのメソッドを呼び出すということです - あなたはは、UIスレッド以外のスレッドからは許可されていません。
デリゲートは基本的にインラインのAction
またはFunc<T>
です。実行しているメソッドの範囲外、またはlambda
式(=>
)を使用してデリゲートを宣言できます。メソッド内でデリゲートを実行するので、現在のウィンドウ/アプリケーションに対して実行されているスレッドで実行します。これは少し太字で表示されています。
ラムダの例
int AddFiveToNumber(int number)
{
var d = (int i => i + 5);
d.Invoke(number);
}
つまり、渡したデリゲートは、コントロールオブジェクトを作成したスレッド(UIスレッド)で実行されます。
アプリケーションがマルチスレッドで、UIスレッド以外のスレッドから何らかのUI操作を実行する場合は、このメソッドを呼び出す必要があります。別のスレッドからコントロールのメソッドを呼び出そうとすると、 System.InvalidOperationException。