C#イベントは同期ですか?


104

この質問には2つの部分があります。

  1. 引き上げイベントがスレッドをブロックし、またはそれは、非同期のEventHandlerの実行を開始しないと行くのスレッドが同時にオンに続けて?

  2. されている個々のEventHandler(イベントにサブスクライブ)を同期次々に実行、または彼らは他の人が同時に実行されていないことを保証なしで非同期で実行されていますか?

回答:


37

あなたの質問に答えるには:

  1. イベントハンドラーがすべて同期的に実装されている場合、イベントを発生させるとスレッドがブロックされます。
  2. イベントハンドラーは、イベントにサブスクライブされた順序で、次々に順次実行されます。

私も内部メカニズムeventとそれに関連する操作に興味がありました。そのため、私は単純なプログラムを作成し、ildasmその実装をざっと見てきました。

短い答えは

  • イベントのサブスクライブまたは呼び出しに関与する非同期操作はありません。
  • イベントは、同じデリゲート型のバッキングデリゲートフィールドで実装されています
  • 購読はで行われます Delegate.Combine()
  • 購読解除はで行われます Delegate.Remove()
  • 呼び出しは、最終的に結合されたデリゲートを呼び出すだけで実行されます。

これが私がしたことです。私が使用したプログラム:

public class Foo
{
    // cool, it can return a value! which value it returns if there're multiple 
    // subscribers? answer (by trying): the last subscriber.
    public event Func<int, string> OnCall;
    private int val = 1;

    public void Do()
    {
        if (OnCall != null) 
        {
            var res = OnCall(val++);
            Console.WriteLine($"publisher got back a {res}");
        }
    }
}

public class Program
{
    static void Main(string[] args)
    {
        var foo = new Foo();

        foo.OnCall += i =>
        {
            Console.WriteLine($"sub2: I've got a {i}");
            return "sub2";
        };

        foo.OnCall += i =>
        {
            Console.WriteLine($"sub1: I've got a {i}");
            return "sub1";
        };

        foo.Do();
        foo.Do();
    }
}

Fooの実装は次のとおりです。

ここに画像の説明を入力してください

フィールド OnCallイベント があることに注意してくださいOnCall。フィールドOnCallは明らかにバッキングプロパティです。そして、これは単なる、Func<int, string>派手なものではありません。

興味深い部分は次のとおりです。

  • add_OnCall(Func<int, string>)
  • remove_OnCall(Func<int, string>)
  • でどのようOnCallに呼び出されるかDo()

購読と購読解除はどのように実装されますか?

add_OnCallCILでの簡略化された実装を以下に示します。興味深い部分は、Delegate.Combine2つのデリゲートを連結するために使用することです。

.method public hidebysig specialname instance void 
        add_OnCall(class [mscorlib]System.Func`2<int32,string> 'value') cil managed
{
  // ...
  .locals init (class [mscorlib]System.Func`2<int32,string> V_0,
           class [mscorlib]System.Func`2<int32,string> V_1,
           class [mscorlib]System.Func`2<int32,string> V_2)
  IL_0000:  ldarg.0
  IL_0001:  ldfld      class [mscorlib]System.Func`2<int32,string> ConsoleApp1.Foo::OnCall
  // ...
  IL_000b:  call       class [mscorlib]System.Delegate [mscorlib]System.Delegate::Combine(class [mscorlib]System.Delegate,
                                                                                          class [mscorlib]System.Delegate)
  // ...
} // end of method Foo::add_OnCall

同様に、Delegate.Removeで使用されremove_OnCallます。

イベントはどのように呼び出されますか?

OnCallin を呼び出すにはDo()、argをロードした後に、最後の連結デリゲートを呼び出すだけです。

IL_0026:  callvirt   instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)

サブスクライバーはどのくらい正確にイベントにサブスクライブしますか?

そして最後に、Main当然のことながら、OnCallイベントのサブスクライブadd_OnCallFooインスタンスのメソッドを呼び出すことによって行われます。


3
よくやった!!この質問をしてから久しぶりです。私の2つの部分の質問に直接答える言い回しを上に配置できる場合(つまり、「#1の答えはノー、#2の答えはノー」)、これを公式の答えにします。私は元の質問に答えるためのすべての部分としてあなたの投稿を賭けていますが、私はC#をもう使用しないので(そして他のGoogle社員はこれらの概念に不慣れな可能性があります)、そのため、答えを明確にする言い回しを求めています。
アレクサンダーバード

@AlexanderBirdに感謝します。編集して回答を一番上に配置します。
KFL、2017

@KFL、それはまだはっきりしていません、私はアレックスと同じコメントを残そうとしていました。単純な「はい、同期しています」が役に立ちます
ジョニー5

71

これは一般的な答えであり、デフォルトの動作を反映しています。

  1. はい、イベントにサブスクライブするメソッドが非同期でない場合、スレッドをブロックします。
  2. それらは次々に実行されます。これには別のひねりがあります。あるイベントハンドラーが例外をスローした場合、まだ実行されていないイベントハンドラーは実行されません。

そうは言っても、イベントを提供するすべてのクラスは、そのイベントを非同期的に実装することを選択できます。IDesignは、EventsHelperこれを簡略化するというクラスを提供します。

[注意]このリンクでは、EventsHelperクラスをダウンロードするために電子メールアドレスを提供する必要があります。(私はいかなる方法でも提携していません)


私はいくつかのフォーラム投稿を読みましたが、そのうち2つは最初のポイントに矛盾していますが、適切な理由はありません。私はあなたの答えを疑うことはありません(それは私がこれまでに経験したものと一致します)最初の点に関する公式のドキュメントはありますか?私はこれについて確信する必要がありますが、私はこの正確な問題で公式な何かを見つけるのが難しいです。
アダムLS

@ AdamL.S。イベントの呼び出し方法の問題です。したがって、それは実際にイベントを提供するクラスに依存します。
Daniel Hilgarth 2017

14

イベントにサブスクライブしたデリゲートは、追加された順序で同期的に呼び出されます。デリゲートの1つが例外をスローした場合、後続のデリゲートは呼び出されません。

イベントはマルチキャストデリゲートで定義されているため、次のコマンドを使用して独自の起動メカニズムを作成できます

Delegate.GetInvocationList();

デリゲートを非同期に呼び出す。




3

C#のイベントは、2番目のスレッドを手動で開始しない限り、(両方の場合で)同期的に実行されます。


非同期イベントハンドラーの使用についてはどうですか?その後、別のスレッドで実行されますか?「非同期」について聞いたことがありますが、非同期イベントハンドラーには独自のスレッドがあるようです。わかりません:/教えてくれませんか?
Winger Sendon、2016

3

イベントは同期しています。これが、イベントのライフサイクルが機能する理由です。ロードはロードの前に発生し、ロードはレンダリングの前に発生します。

イベントにハンドラーが指定されていない場合、サイクルは完全に処理されます。複数のハンドラーが指定されている場合、それらは順番に呼び出され、一方が完全に終了するまで一方を続行できません。

非同期呼び出しでさえ、ある程度同期しています。開始が完了する前に終了を呼び出すことはできません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.