Are C# Events Synchronous

Are C# events synchronous?

Yes, they are synchronous.

To answer your questions:

  1. Raising an event does block the thread if the event handlers are all implemented synchronously.
  2. 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:

Sample Image

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 in Do()

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



Leave a reply



Submit