この質問には2つの部分があります。
raisingイベントはスレッドをブロックしますか、それともEventHandlerの実行を非同期に開始し、スレッドが同時に継続しますか?
個別のEventHandlers(イベントにサブスクライブ)は同期して次々に実行されますか?
質問に答えるには:
私も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の実装は次のとおりです。
fieldOnCall
とeventOnCall
があることに注意してください。フィールドOnCall
は明らかにバッキングプロパティです。そして、それは単に_Func<int, string>
_であり、ここでは特に素晴らしいものではありません。
興味深い部分は次のとおりです。
add_OnCall(Func<int, string>)
remove_OnCall(Func<int, string>)
Do()
でOnCall
を呼び出す方法以下は、CILでの_add_OnCall
_の短縮実装です。興味深い部分は、2つのデリゲートを連結するために _Delegate.Combine
_ を使用していることです。
_.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
_で使用されます。
Do()
でOnCall
を呼び出すには、引数を読み込んだ後、最終的な連結デリゲートを呼び出します。
_IL_0026: callvirt instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)
_
そして最後に、Main
では、驚くことではなく、OnCall
イベントをサブスクライブするには、Foo
インスタンスで_add_OnCall
_メソッドを呼び出します。
これは一般的な答えであり、デフォルトの動作を反映しています。
そうは言っても、イベントを提供するすべてのクラスは、イベントを非同期に実装することを選択できます。 IDesign は、これを簡素化する EventsHelper
というクラスを提供します。
[注]このリンクでは、電子メールを入力する必要がありますEventsHelperクラスをダウンロードするアドレス。 (私はいかなる形でも提携していません)
イベントにサブスクライブされたデリゲートは、追加された順に同期的に呼び出されます。デリゲートの1つが例外をスローした場合、will notに続くものが呼び出されます。
イベントはマルチキャストデリゲートで定義されているため、次を使用して独自の起動メカニズムを作成できます。
Delegate.GetInvocationList();
デリゲートを非同期に呼び出します。
イベントはデリゲートの配列です。デリゲート呼び出しが同期的である限り、イベントも同期的です。
一般に、イベントは同期的です。ただし、ThreadPool
がnullの場合、SyncronisingObject
スレッドでSystem.Timers.Timer.Elapsed
イベントが発生するなど、いくつかの例外があります。
ドキュメント: http://msdn.Microsoft.com/en-us/library/system.timers.timer.elapsed.aspx
2番目のスレッドを手動で開始しない限り、C#のイベントは同期的に実行されます(両方の場合)。
イベントは同期的です。これが、イベントライフサイクルがそのように機能する理由です。初期化はロードの前に発生し、ロードはレンダリングの前に発生します。
イベントにハンドラが指定されていない場合、サイクルはただ燃え上がります。複数のハンドラーが指定されている場合、それらは順番に呼び出され、もう一方のハンドラーが完全に終了するまで続行できません。
非同期呼び出しでもある程度同期します。開始が完了する前に終了を呼び出すことは不可能です。