この質問には2つの部分があります。
引き上げイベントがスレッドをブロックし、またはそれは、非同期のEventHandlerの実行を開始しないと行くのスレッドが同時にオンに続けて?
されている個々のEventHandler(イベントにサブスクライブ)を同期次々に実行、または彼らは他の人が同時に実行されていないことを保証なしで非同期で実行されていますか?
この質問には2つの部分があります。
引き上げイベントがスレッドをブロックし、またはそれは、非同期のEventHandlerの実行を開始しないと行くのスレッドが同時にオンに続けて?
されている個々のEventHandler(イベントにサブスクライブ)を同期次々に実行、または彼らは他の人が同時に実行されていないことを保証なしで非同期で実行されていますか?
回答:
あなたの質問に答えるには:
私も内部メカニズム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_OnCall
CILでの簡略化された実装を以下に示します。興味深い部分は、Delegate.Combine
2つのデリゲートを連結するために使用することです。
.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
ます。
OnCall
in を呼び出すにはDo()
、argをロードした後に、最後の連結デリゲートを呼び出すだけです。
IL_0026: callvirt instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)
そして最後に、Main
当然のことながら、OnCall
イベントのサブスクライブadd_OnCall
はFoo
インスタンスのメソッドを呼び出すことによって行われます。
これは一般的な答えであり、デフォルトの動作を反映しています。
そうは言っても、イベントを提供するすべてのクラスは、そのイベントを非同期的に実装することを選択できます。IDesignは、EventsHelper
これを簡略化するというクラスを提供します。
[注意]このリンクでは、EventsHelperクラスをダウンロードするために電子メールアドレスを提供する必要があります。(私はいかなる方法でも提携していません)
イベントはデリゲートの単なる配列です。デリゲート呼び出しが同期である限り、イベントも同期です。
一般に、イベントは同期しています。ただし、がnullの場合にスレッドでSystem.Timers.Timer.Elapsed
イベントが発生するなど、いくつかの例外があります。ThreadPool
SyncronisingObject
ドキュメント:http : //msdn.microsoft.com/en-us/library/system.timers.timer.elapsed.aspx
C#のイベントは、2番目のスレッドを手動で開始しない限り、(両方の場合で)同期的に実行されます。
イベントは同期しています。これが、イベントのライフサイクルが機能する理由です。ロードはロードの前に発生し、ロードはレンダリングの前に発生します。
イベントにハンドラーが指定されていない場合、サイクルは完全に処理されます。複数のハンドラーが指定されている場合、それらは順番に呼び出され、一方が完全に終了するまで一方を続行できません。
非同期呼び出しでさえ、ある程度同期しています。開始が完了する前に終了を呼び出すことはできません。