Reputation: 40639
There are two parts to this question:
Does raising an event block the thread, or does it start execution of EventHandlers asynchronously and the thread goes continues on at the same time?
Are the individual EventHandlers (subscribed to the event) run synchronously one after another, or are they run asynchronously with no guarantee that others aren't running at the same time?
Upvotes: 127
Views: 37455
Reputation: 174309
This is a general answer and reflects the default behavior:
Having said that, every class that provides events can choose to implement its event asynchronously. IDesign provides a class called EventsHelper
that simplifies this.
[Note] this link requires you to provide an e-mail address to download the EventsHelper class. (I am not affiliated in any way)
Upvotes: 83
Reputation: 17850
Yes, they are synchronous.
To answer your questions:
I too was curious about the internal mechanism of event
and its related operations. So I wrote a simple program and used ildasm
to poke around its implementation.
The short answer is
Delegate.Combine()
Delegate.Remove()
Here's what I did. The program I used:
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();
}
}
Here's Foo's implementation:
Note that there is a field OnCall
and an event OnCall
. The field OnCall
is obviously the backing property. And it's merely a Func<int, string>
, nothing fancy here.
Now the interesting parts are:
add_OnCall(Func<int, string>)
remove_OnCall(Func<int, string>)
OnCall
is invoked in Do()
Here's the abbreviated add_OnCall
implementation in CIL. The interesting part is it uses Delegate.Combine
to concatenate two delegates.
.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
Likewise, Delegate.Remove
is used in remove_OnCall
.
To invoke OnCall
in Do()
, it simply calls the final concatenated delegate after loading the arg:
IL_0026: callvirt instance !1 class [mscorlib]System.Func`2<int32,string>::Invoke(!0)
And finally, in Main
, not suprisingly, subscribing to the OnCall
event is done by calling add_OnCall
method on the Foo
instance.
Upvotes: 69
Reputation: 3689
The delegates subscribed to the event are invoked synchronously in the order they were added. If one of the delegates throws an exception, the ones following will not be called.
Since events are defined with multicast delegates, you can write your own firing mechanism using
Delegate.GetInvocationList();
and invoking the delegates asynchronously;
Upvotes: 15
Reputation: 37533
Events are synchronous. This is why the event lifecycle works the way it does. Inits happen before loads, loads happen before renders etc.
If no handler is specified for an event, the cycle just blazes through. If more than one handler is specified, they will be called in order and one can't continue until the other is completely finished.
Even asynchronous calls are synchronous to a degree. It would be impossible to call the end before the begin is completed.
Upvotes: 4
Reputation: 20044
Events in C# run synchronously (in both cases), as long as you don't start a second thread manually.
Upvotes: 4
Reputation: 136104
In general, events are synchronous. However there are some exceptions, such as System.Timers.Timer.Elapsed
event being raised on a ThreadPool
thread if SyncronisingObject
is null.
Docs: http://msdn.microsoft.com/en-us/library/system.timers.timer.elapsed.aspx
Upvotes: 9
Reputation: 7694
Events are just arrays of delegates. As long as delegate call is synchronous, events are also synchronous.
Upvotes: 13