Are C# events synchronous?
Yes, they are synchronous.
To answer your questions:
- Raising an event does block the thread if the event handlers are all implemented synchronously.
- The event handlers are executed sequentially, one after another, in the order they are subscribed to the event.
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
- there's no asynchronous operation involved in subscribing or invoking the events.
- event is implemented with a backing delegate field of the same delegate type
- subscribing is done with
Delegate.Combine()
- unsubscribing is done with
Delegate.Remove()
- Invoking is done by simply invoking the final combined delegate
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>)
- and how
OnCall
is invoked inDo()
How is Subscribing and Unsubscribing Implemented?
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
.
How is an event invoked?
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)
How exactly does a subscriber subscribe to an event?
And finally, in Main
, not suprisingly, subscribing to the OnCall
event is done by calling add_OnCall
method on the Foo
instance.
c# do events run synchronous?
For the question whether events run asynchronously in C#, a similar answer applies as to so many questions: It is up to you (or whoever wrote the class whose events you want to consume). The default way to implement an event in C# is that events are fired in the same thread that caused the "event" that the event represents. However, you can just implement this part differently and implement asynchronous events.
The synchronous implementation looks like this:
protected virtual void OnFoo(EventArgs e)
{
if (Foo != null) Foo(this, e);
}
The asynchronous implementation would look like this:
protected virtual void OnFooAsync(EventArgs e)
{
if (Foo != null) Foo.BeginInvoke(this, e);
}
C# Asynchronous events for multiple event handlers
If you don't want to be blocked by the event handlers, you can invoke them from a ThreadPool
thread, with Task.Run
:
public void Receive(object myData)
{
// some processing
var handler = OnReceive;
if (handler != null)
{
var fireAndForget = Task.Run(handler.Invoke(new MyEventArgs { Data = myData }));
}
}
The implication is that any exceptions thrown by the handlers will remain unobserved.
How Synchronous/blocking are Events/Delegate subscriptions?
It looks like you'd benefit from parallelisation of your event handlers, as they will indeed execute sequentially and synchronously.
To create your custom event handlers you might write:
public event FooDelegate FooEvent
{
add
{
// code to add an event handler
}
remove
{
// code to remove an event handler
}
}
See the following questions for more information on what to do with the custom event handlers:
- Events with Task Parallel Library for .NET 4+
- How to process events in parallel
- Parallel event handling in C#
- etc.
and Jon Skeet's page on delegates and events.
C# Event execution order (Unity3D game)
Yes, synchronous event handlers as shown in the post are guaranteed to finish after executing sequentially (in some order - Order of event handler execution) before execution returns back to code that triggered event.
So your code is guaranteed to print A,B,C.
More links: are C# events synchronous?, making event asynchronous - Should I avoid 'async void' event handlers? , awaiting for asynchronous events - Asynchronous events in C#.
C# How to make synchronous method waiting for events to fire?
Try use ManualResetEvent:
var wait = new ManualResetEvent(false);
var handler = new EventHandler((o, e) => wait.Set());
MyAsyncMethod(data, handler); // so it started and will fire handler soon
wait.WaitOne();
Do MVC events overlap?
I found the answer in another post: are C# events synchronous?
It says that an event or the combined backing delegate is called via the Invoke method rather than the InvokeAsync method which means that by default .Net events are called synchronously.
There is one exception:
Raising an event does block the thread if the event handlers are all implemented synchronously.
This means that the inner HttpApplication is calling the attached handlers one after another and then goes on to the next event if all events are synchronous. This makes the changes made in one handler overridable in another handler which is added later to the event.
The event handlers are executed sequentially, one after another, in the order they are subscribed to the event.
Since I know the important MVC event handlers are synchronous this should not be a problem. But as soon as one attached event handler is aysnc the combined delegate (backing event delegate) is run async.
class Program
{
static void Main(string[] args)
{
var foo = new Foo();
foo.OnWork += async (e, v) =>
{
await Task.Run(() =>
{
Thread.Sleep(1000);
Console.WriteLine(v);
});
};
foo.OnWork += (e, v) =>
{
Console.WriteLine("2." + v);
};
foo.DoWork();
foo.DoWork();
Console.ReadLine();
}
}
public class Foo
{
public event EventHandler<int> OnWork;
private int val = 1;
public void DoWork()
{
OnWork?.Invoke(this, val++);
}
}
// Output:
// 2.1
// 2.2
// 1
// 2
// Sometimes 1 and 2 are reversed because they are running in different Threads
How do I await events in C#?
Personally, I think that having async
event handlers may not be the best design choice, not the least of which reason being the very problem you're having. With synchronous handlers, it's trivial to know when they complete.
That said, if for some reason you must or at least are strongly compelled to stick with this design, you can do it in an await
-friendly way.
Your idea to register handlers and await
them is a good one. However, I would suggest sticking with the existing event paradigm, as that will keep the expressiveness of events in your code. The main thing is that you have to deviate from the standard EventHandler
-based delegate type, and use a delegate type that returns a Task
so that you can await
the handlers.
Here's a simple example illustrating what I mean:
class A
{
public event Func<object, EventArgs, Task> Shutdown;
public async Task OnShutdown()
{
Func<object, EventArgs, Task> handler = Shutdown;
if (handler == null)
{
return;
}
Delegate[] invocationList = handler.GetInvocationList();
Task[] handlerTasks = new Task[invocationList.Length];
for (int i = 0; i < invocationList.Length; i++)
{
handlerTasks[i] = ((Func<object, EventArgs, Task>)invocationList[i])(this, EventArgs.Empty);
}
await Task.WhenAll(handlerTasks);
}
}
The OnShutdown()
method, after doing the standard "get local copy of the event delegate instance", first invokes all of the handlers, and then awaits all of the returned Tasks
(having saved them to a local array as the handlers are invoked).
Here's a short console program illustrating the use:
class Program
{
static void Main(string[] args)
{
A a = new A();
a.Shutdown += Handler1;
a.Shutdown += Handler2;
a.Shutdown += Handler3;
a.OnShutdown().Wait();
}
static async Task Handler1(object sender, EventArgs e)
{
Console.WriteLine("Starting shutdown handler #1");
await Task.Delay(1000);
Console.WriteLine("Done with shutdown handler #1");
}
static async Task Handler2(object sender, EventArgs e)
{
Console.WriteLine("Starting shutdown handler #2");
await Task.Delay(5000);
Console.WriteLine("Done with shutdown handler #2");
}
static async Task Handler3(object sender, EventArgs e)
{
Console.WriteLine("Starting shutdown handler #3");
await Task.Delay(2000);
Console.WriteLine("Done with shutdown handler #3");
}
}
Having gone through this example, I now find myself wondering if there couldn't have been a way for C# to abstract this a bit. Maybe it would have been too complicated a change, but the current mix of the old-style void
-returning event handlers and the new async
/await
feature does seem a bit awkward. The above works (and works well, IMHO), but it would have been nice to have better CLR and/or language support for the scenario (i.e. be able to await a multicast delegate and have the C# compiler turn that into a call to WhenAll()
).
Related Topics
How to Get the Correct Ip from Http_X_Forwarded_For If It Contains Multiple Ip Addresses
Custom JSON Serialization for Each Item in Ienumerable
Calling a Method in Parent Page from User Control
Login Using Google Oauth 2.0 with C#
How to Write Output from a Unit Test
How to Implement Solid Principles into an Existing Project
How to Import from Excel to a Dataset Using Microsoft.Office.Interop.Excel
Using Profiles in Automapper to Map the Same Types with Different Logic
Main: Not All Code Paths Return a Value
Convert String to Int Array Using Linq
Give Some Command to View in Mvvm
Retrieve the Respective Coordinates of All Words on the Page with Itextsharp
C# Generic Type Inference with Multiple Types
Dbset.Attach(Entity) VS Dbcontext.Entry(Entity).State = Entitystate.Modified
Differencebetween Casting and Coercing
Visual Studio 2013 Doesn't Discover Unit Tests