98

誰でもこのリンクに書かれたこの声明を説明できますか

Invoke(Delegate):

コントロールの基になるウィンドウ ハンドルを所有するスレッドで、指定されたデリゲートを実行します。

これが何を意味するのか誰か説明できますか (特に太字のもの) はっきりと理解できません

4

9 に答える 9

136

この質問に対する答えは、C# コントロールの仕組みにあります。

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

Control.InvokeRequiredから

Invoke が実際に行っていることは、呼び出しているコードが、コントロールが "存在する" スレッド上で発生し、クロススレッドの例外を効果的に防止することです。

歴史的な観点から、.Net 1.1 では、これは実際に許可されていました。つまり、どのバックグラウンド スレッドからでも「GUI」スレッドでコードを実行でき、これはほとんどの場合うまくいくということです。他のことをしている間に GUI スレッドを効果的に中断していたために、アプリが終了するだけの場合もありました。これはクロス スレッド例外です。GUI が何か他のものを描画しているときに TextBox を更新しようとすることを想像してください。

  • どのアクションが優先されますか?
  • 両方が同時に起こる可能性さえありますか?
  • GUI が実行する必要がある他のすべてのコマンドはどうなりますか?

事実上、キューを中断しているため、多くの予期しない結果が生じる可能性があります。Invoke は、実行したいことをそのキューに入れるための「礼儀正しい」方法であり、このルールは、スローされたInvalidOperationExceptionを介して .Net 2.0 以降で適用されました。

バックグラウンドで実際に何が行われているか、および「GUI スレッド」の意味を理解するには、メッセージ ポンプまたはメッセージ ループとは何かを理解することが役に立ちます。

これは実際には「メッセージ ポンプとは」という質問ですでに回答されており、コントロールと対話するときに結び付けようとしている実際のメカニズムを理解するために読むことをお勧めします。

役に立つと思われるその他の読み物には、次のものがあります。

Begin Invoke について

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 の質問Clean Up Code Littered with Invoke Required で適切にカバーされています。

興味深いかもしれない歴史的に何が起こったのかについてのさらなる記述もあります.

于 2013-02-05T09:15:23.077 に答える
73

Windows フォームのコントロールまたはウィンドウ オブジェクトは、ハンドル(HWND とも呼ばれる)によって識別される Win32 ウィンドウの単なるラッパーです。コントロールで行うほとんどの操作は、最終的にこのハンドルを使用する Win32 API 呼び出しになります。ハンドルはそれを作成したスレッド (通常はメイン スレッド) によって所有され、別のスレッドによって操作されるべきではありません。何らかの理由で、別のスレッドからの制御を使用して何かを行う必要がある場合は、 を使用Invokeして、メイン スレッドに代わりにそれを実行するように依頼できます。

たとえば、ラベルのテキストをワーカー スレッドから変更したい場合は、次のようにすることができます。

theLabel.Invoke(new Action(() => theLabel.Text = "hello world from worker thread!"));
于 2013-02-05T09:13:20.013 に答える
27

コントロールを変更する場合は、コントロールが作成されたスレッドで行う必要があります。この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"));
}
于 2013-02-05T09:20:31.170 に答える
8
Invoke((MethodInvoker)delegate{ textBox1.Text = "Test"; });
于 2016-12-12T00:47:27.060 に答える
2

実際には、デリゲートがメイン スレッドで呼び出されることが保証されていることを意味します。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)
}
于 2013-02-05T09:14:15.933 に答える
2

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;
        }
    }

メイン スレッドで実行される Threadpool スレッド (つまりワーカー スレッド) を作成しても状況があります。メインスレッドがさらに命令を処理するために利用できるため、新しいスレッドは作成されません。したがって、まず現在実行中のスレッドがメイン スレッドであるかどうかを調べthis.InvokeRequiredます。true を返す場合、現在のコードはワーカー スレッドで実行されているため、this.Invoke(d, new object[] { text }); を呼び出します。

それ以外の場合は、UI コントロールを直接更新します (ここでは、メイン スレッドでコードを実行していることが保証されます)。

于 2014-02-10T05:49:35.043 に答える
1

これは、バックグラウンド ワーカーまたはスレッド プール スレッドからそのメソッドを呼び出した場合でも、デリゲートが UI スレッドで実行されることを意味します。UI 要素にはスレッド アフィニティがあります。それらは 1 つのスレッド (UI スレッド) と直接やり取りすることのみを好みます。UI スレッドは、コントロール インスタンスを作成したスレッドとして定義されるため、ウィンドウ ハンドルに関連付けられます。しかし、それはすべて実装の詳細です。

重要な点は、このメソッドをワーカー スレッドから呼び出して、UI にアクセスできるようにすることです (ラベルの値を変更するなど) 。これは、UI スレッド以外のスレッドからはアクセスできないためです。

于 2013-02-05T09:13:45.507 に答える
0

デリゲートは本質的にインラインActionの orFunc<T>です。実行中またはlambda式 ( =>) を使用しているメソッドのスコープ外でデリゲートを宣言できます。メソッド内でデリゲートを実行するため、太字で示されている現在のウィンドウ/アプリケーションに対して実行されているスレッドで実行します。

ラムダの例

int AddFiveToNumber(int number)
{
  var d = (int i => i + 5);
  d.Invoke(number);
}
于 2013-02-05T09:13:32.903 に答える
0

これは、渡したデリゲートが Control オブジェクトを作成したスレッド (UI スレッド) で実行されることを意味します。

アプリケーションがマルチスレッド化されていて、UI スレッド以外のスレッドから UI 操作を実行したい場合は、このメソッドを呼び出す必要があります。別のスレッドから Control のメソッドを呼び出そうとすると、 System.InvalidOperationException.

于 2013-02-05T09:14:29.250 に答える