Action vs EventHandler vs Func for async/awaited method calls - c#

I have async/awaited methods in my Connected anonymous method implementation and I wonder which one of these is the best to use in my case. I put examples below and added information according to what I know. Are my statements correct? Anything else important that I'm missing?
EventHandler
The only disadvantage to me is that the anonymous method is basically async void. Yes, we can have async calls, but they will be kinda "fire and forget/crash".
public class Test
{
public event EventHandler<ConnectEventArgs>? Connected;
public ValueTask StartAsync()
{
Connected?.Invoke(this, new ConnectEventArgs(...))
}
}
var test = new Test();
test.Connected += async (sender, e) => // private async void OnConnect(object? sender, ConnectEventArgs e) => BAD
{
await SendAsync(); // fire and forget
};
await test.StartAsync();
Func
What I notice is that we can have async calls without having to worry about async void. I think that's the only good way to have async/awaited method calls. Isn't it?
public class Test
{
public event Func<Task>? Connected;
public ValueTask StartAsync()
{
await (Connected?.Invoke() ?? Task.CompletedTask).ConfigureAwait(false);
}
}
var test = new Test();
test.Connected += async () => // private async Task OnConnect()
{
await SendAsync(); // Should be okay?
};
await test.StartAsync();
Action
The only difference between Action and EventHandler that comes into my mind is that we don't pass the sender and we don't have to create EventArgs classes.
public class Test
{
public event Action<bool>? Connected;
public ValueTask StartAsync()
{
Connected?.Invoke(true);
}
}
var test = new Test();
test.Connected += async (boolean) => // private void OnConnect(bool boolean)
{
await SendAsync(); // fire and forget
};
await test.StartAsync();

Related

Call async Method from a normal method c#

I am trying to call an async method from a normal method. I have the following two issues here:
When I use Task.Run in class A to class B's method, JQuery is not
receiving the call.
I cannot use await because it throws this error:
The await can only be used in an async method
I am not sure what the issue is with respect to the first problem I am having. Secondly, is there a way to use await when calling classB because it seems to work when I made a call from a different method that is async.
public class A :IA
{
public A ()
{
consumer.Received += Received;
}
public void Received(object sender, BasicDeliverEventArgs ea)
{
//get message here and send to classB
Task.Run(() => classB.PingMesssage(Message));
}
}
public class B
{
public async Task PingMesssage(string message)
{
await InvokeClientMethodToAllAsync("callJQueryMethod", "message"); //calling js here
}
}
You can make Received async and await a call to PingMessage:
public async void Received(object sender, BasicDeliverEventArgs ea) {
await classB.PingMessage(message);
}

SendOrPostCallback behaviour

SendOrPostCallback Represents a method to be called when a message is to be dispatched to a synchronization context. In first case SendOrPostCallback points on async method that i guess should be executed synchronously. What will happen if Delegate will point on async delegate? How behaviour should change?
Before changes:
public class ViewModel
{
public ViewModel()
{
SynchronizationContext.Current.Post(new SendOrPostCallback(SomeMethods), null);
}
private async void SomeMethods(object obj)
{
await Foo(obj);
}
private async Task Foo(object obj)
{
bool Canceled = false;
while (!Canceled)
{
await Task.Delay(3000);
//...
}
}
}
After changes:
public class ViewModelImproved
{
public ViewModelImproved()
{
SynchronizationContext.Current.Post(new SendOrPostCallback(async (obj) => { await SomeMethods(obj); }), null);
}
private async Task SomeMethods(object obj)
{
await Foo(obj);
}
private async Task Foo(object obj)
{
bool Canceled = false;
while (!Canceled)
{
await Task.Delay(3000);
}
//...
}
}
There's no substantial difference. In the first code, there's an async void, and in the second code, there's an async void. In the second code, the async void is hidden within a lambda expression.
Generally, you should avoid async void. In this particular case, it may be OK, since you're dealing with a UI context.

Is there an async version of the MulticastDelegate [duplicate]

I am creating a class that has a series of events, one of them being GameShuttingDown. When this event is fired, I need to invoke the event handler. The point of this event is to notify users the game is shutting down and they need to save their data. The saves are awaitable, and events are not. So when the handler gets called, the game shuts down before the awaited handlers can complete.
public event EventHandler<EventArgs> GameShuttingDown;
public virtual async Task ShutdownGame()
{
await this.NotifyGameShuttingDown();
await this.SaveWorlds();
this.NotifyGameShutDown();
}
private async Task SaveWorlds()
{
foreach (DefaultWorld world in this.Worlds)
{
await this.worldService.SaveWorld(world);
}
}
protected virtual void NotifyGameShuttingDown()
{
var handler = this.GameShuttingDown;
if (handler == null)
{
return;
}
handler(this, new EventArgs());
}
Event registration
// The game gets shut down before this completes because of the nature of how events work
DefaultGame.GameShuttingDown += async (sender, args) => await this.repo.Save(blah);
I understand that the signature for events are void EventName and so making it async is basically fire and forget. My engine makes heavy use of eventing to notify 3rd party developers (and multiple internal components) that events are taking place within the engine and letting them react to them.
Is there a good route to go down to replace eventing with something asynchronous based that I can use? I'm not sure if I should be using BeginShutdownGame and EndShutdownGame with callbacks, but that's a pain because then only the calling source can pass a callback, and not any 3rd party stuff that plugs in to the engine, which is what I am getting with events. If the server calls game.ShutdownGame(), there's no way for engine plugins and or other components within the engine to pass along their callbacks, unless I wire up some kind of registration method, keeping a collection of callbacks.
Any advice on what the preferred/recommended route to go down with this would be greatly appreciated! I have looked around and for the most part what I've seen is using the Begin/End approach which I don't think will satisfy what I'm wanting to do.
Edit
Another option I'm considering is using a registration method, that takes an awaitable callback. I iterate over all of the callbacks, grab their Task and await with a WhenAll.
private List<Func<Task>> ShutdownCallbacks = new List<Func<Task>>();
public void RegisterShutdownCallback(Func<Task> callback)
{
this.ShutdownCallbacks.Add(callback);
}
public async Task Shutdown()
{
var callbackTasks = new List<Task>();
foreach(var callback in this.ShutdownCallbacks)
{
callbackTasks.Add(callback());
}
await Task.WhenAll(callbackTasks);
}
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()).
Peter's example is great, I've just simplified it a little using LINQ and extensions:
public static class AsynchronousEventExtensions
{
public static Task Raise<TSource, TEventArgs>(this Func<TSource, TEventArgs, Task> handlers, TSource source, TEventArgs args)
where TEventArgs : EventArgs
{
if (handlers != null)
{
return Task.WhenAll(handlers.GetInvocationList()
.OfType<Func<TSource, TEventArgs, Task>>()
.Select(h => h(source, args)));
}
return Task.CompletedTask;
}
}
It may be a good idea to add a timeout. To raise the event call Raise extension:
public event Func<A, EventArgs, Task> Shutdown;
private async Task SomeMethod()
{
...
await Shutdown.Raise(this, EventArgs.Empty);
...
}
But you have to be aware that, unlike synchronous evens, this implementation calls handlers concurrently. It can be an issue if handlers have to be executed strictly consecutively what they are often do, e.g. a next handler depends on results of the previous one:
someInstance.Shutdown += OnShutdown1;
someInstance.Shutdown += OnShutdown2;
...
private async Task OnShutdown1(SomeClass source, MyEventArgs args)
{
if (!args.IsProcessed)
{
// An operation
await Task.Delay(123);
args.IsProcessed = true;
}
}
private async Task OnShutdown2(SomeClass source, MyEventArgs args)
{
// OnShutdown2 will start execution the moment OnShutdown1 hits await
// and will proceed to the operation, which is not the desired behavior.
// Or it can be just a concurrent DB query using the same connection
// which can result in an exception thrown base on the provider
// and connection string options
if (!args.IsProcessed)
{
// An operation
await Task.Delay(123);
args.IsProcessed = true;
}
}
You'd better change the extension method to call handlers consecutively:
public static class AsynchronousEventExtensions
{
public static async Task Raise<TSource, TEventArgs>(this Func<TSource, TEventArgs, Task> handlers, TSource source, TEventArgs args)
where TEventArgs : EventArgs
{
if (handlers != null)
{
foreach (Func<TSource, TEventArgs, Task> handler in handlers.GetInvocationList())
{
await handler(source, args);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Example
{
// delegate as alternative standard EventHandler
public delegate Task AsyncEventHandler<TEventArgs>(object sender, TEventArgs e, CancellationToken token);
public class ExampleObject
{
// use as regular event field
public event AsyncEventHandler<EventArgs> AsyncEvent;
// invoke using the extension method
public async Task InvokeEventAsync(CancellationToken token) {
await this.AsyncEvent.InvokeAsync(this, EventArgs.Empty, token);
}
// subscribe (add a listener) with regular syntax
public static async Task UsageAsync() {
var item = new ExampleObject();
item.AsyncEvent += (sender, e, token) => Task.CompletedTask;
await item.InvokeEventAsync(CancellationToken.None);
}
}
public static class AsynEventHandlerExtensions
{
// invoke a async event (with null-checking)
public static async Task InvokeAsync<TEventArgs>(this AsyncEventHandler<TEventArgs> handler, object sender, TEventArgs args, CancellationToken token) {
var delegates = handler?.GetInvocationList();
if (delegates?.Length > 0) {
var tasks = delegates
.Cast<AsyncEventHandler<TEventArgs>>()
.Select(e => e.Invoke(sender, args, token));
await Task.WhenAll(tasks);
}
}
}
}
internal static class EventExtensions
{
public static void InvokeAsync<TEventArgs>(this EventHandler<TEventArgs> #event, object sender,
TEventArgs args, AsyncCallback ar, object userObject = null)
where TEventArgs : class
{
var listeners = #event.GetInvocationList();
foreach (var t in listeners)
{
var handler = (EventHandler<TEventArgs>) t;
handler.BeginInvoke(sender, args, ar, userObject);
}
}
}
example:
public event EventHandler<CodeGenEventArgs> CodeGenClick;
private void CodeGenClickAsync(CodeGenEventArgs args)
{
CodeGenClick.InvokeAsync(this, args, ar =>
{
InvokeUI(() =>
{
if (args.Code.IsNotNullOrEmpty())
{
var oldValue = (string) gv.GetRowCellValue(gv.FocusedRowHandle, nameof(License.Code));
if (oldValue != args.Code)
gv.SetRowCellValue(gv.FocusedRowHandle, nameof(License.Code), args.Code);
}
});
});
}
Note: This is async so the event handler may compromise the UI thread. The event handler (subscriber) should do no UI-work.
It wouldn't make much sense otherwise.
declare your event in your event provider:
public event EventHandler DoSomething;
Invoke event your provider:
DoSomething.InvokeAsync(new MyEventArgs(), this, ar => { callback called when finished (synchronize UI when needed here!) }, null);
subscribe the event by client as you would normally do
It's true, events are inherently un-awaitable so you'll have to work around it.
One solution I have used in the past is using a semaphore to wait for all entries in it to be released. In my situation I only had one subscribed event so I could hardcode it as new SemaphoreSlim(0, 1) but in your case you might want to override the getter/setter for your event and keep a counter of how many subscribers there are so you can dynamically set the max amount of simultaneous threads.
Afterwards you pass a semaphore entry to each of the subscribers and let them do their thing until SemaphoreSlim.CurrentCount == amountOfSubscribers (aka: all spots have been freed).
This would essentially block your program until all event subscribers have finished.
You might also want to consider providing an event à la GameShutDownFinished for your subscribers, which they have to call when they're done with their end-of-game task. Combined with the SemaphoreSlim.Release(int) overload you can now clear up all semaphore entries and simply use Semaphore.Wait() to block the thread. Instead of having to check whether or not all entries have been cleared you now wait until one spot has been freed (but there should only one moment where all spots are freed at once).
I know that the op was asking specifically about using async and tasks for this, but here is an alternative that means the handlers do not need to return a value. The code is based on Peter Duniho's example. First the equivalent class A (squashed up a bit to fit) :-
class A
{
public delegate void ShutdownEventHandler(EventArgs e);
public event ShutdownEventHandler ShutdownEvent;
public void OnShutdownEvent(EventArgs e)
{
ShutdownEventHandler handler = ShutdownEvent;
if (handler == null) { return; }
Delegate[] invocationList = handler.GetInvocationList();
Parallel.ForEach<Delegate>(invocationList,
(hndler) => { ((ShutdownEventHandler)hndler)(e); });
}
}
A simple console application to show its use...
using System;
using System.Threading;
using System.Threading.Tasks;
...
class Program
{
static void Main(string[] args)
{
A a = new A();
a.ShutdownEvent += Handler1;
a.ShutdownEvent += Handler2;
a.ShutdownEvent += Handler3;
a.OnShutdownEvent(new EventArgs());
Console.WriteLine("Handlers should all be done now.");
Console.ReadKey();
}
static void handlerCore( int id, int offset, int num )
{
Console.WriteLine("Starting shutdown handler #{0}", id);
int step = 200;
Thread.Sleep(offset);
for( int i = 0; i < num; i += step)
{
Thread.Sleep(step);
Console.WriteLine("...Handler #{0} working - {1}/{2}", id, i, num);
}
Console.WriteLine("Done with shutdown handler #{0}", id);
}
static void Handler1(EventArgs e) { handlerCore(1, 7, 5000); }
static void Handler2(EventArgs e) { handlerCore(2, 5, 3000); }
static void Handler3(EventArgs e) { handlerCore(3, 3, 1000); }
}
I hope that this is useful to someone.
If you need to await a standard .net event handler you can't do that, because it's void.
But you can create an async event system to handle that:
public delegate Task AsyncEventHandler(AsyncEventArgs e);
public class AsyncEventArgs : System.EventArgs
{
public bool Handled { get; set; }
}
public class AsyncEvent
{
private string name;
private List<AsyncEventHandler> handlers;
private Action<string, Exception> errorHandler;
public AsyncEvent(string name, Action<string, Exception> errorHandler)
{
this.name = name;
this.handlers = new List<AsyncEventHandler>();
this.errorHandler = errorHandler;
}
public void Register(AsyncEventHandler handler)
{
if (handler == null)
throw new ArgumentNullException(nameof(handler));
lock (this.handlers)
this.handlers.Add(handler);
}
public void Unregister(AsyncEventHandler handler)
{
if (handler == null)
throw new ArgumentNullException(nameof(handler));
lock (this.handlers)
this.handlers.Remove(handler);
}
public IReadOnlyList<AsyncEventHandler> Handlers
{
get
{
var temp = default(AsyncEventHandler[]);
lock (this.handlers)
temp = this.handlers.ToArray();
return temp.ToList().AsReadOnly();
}
}
public async Task InvokeAsync()
{
var ev = new AsyncEventArgs();
var exceptions = new List<Exception>();
foreach (var handler in this.Handlers)
{
try
{
await handler(ev).ConfigureAwait(false);
if (ev.Handled)
break;
}
catch(Exception ex)
{
exceptions.Add(ex);
}
}
if (exceptions.Any())
this.errorHandler?.Invoke(this.name, new AggregateException(exceptions));
}
}
And you can declare now your async events:
public class MyGame
{
private AsyncEvent _gameShuttingDown;
public event AsyncEventHandler GameShuttingDown
{
add => this._gameShuttingDown.Register(value);
remove => this._gameShuttingDown.Unregister(value);
}
void ErrorHandler(string name, Exception ex)
{
// handle event error.
}
public MyGame()
{
this._gameShuttingDown = new AsyncEvent("GAME_SHUTTING_DOWN", this.ErrorHandler);.
}
}
And invoke your async event using:
internal async Task NotifyGameShuttingDownAsync()
{
await this._gameShuttingDown.InvokeAsync().ConfigureAwait(false);
}
Generic version:
public delegate Task AsyncEventHandler<in T>(T e) where T : AsyncEventArgs;
public class AsyncEvent<T> where T : AsyncEventArgs
{
private string name;
private List<AsyncEventHandler<T>> handlers;
private Action<string, Exception> errorHandler;
public AsyncEvent(string name, Action<string, Exception> errorHandler)
{
this.name = name;
this.handlers = new List<AsyncEventHandler<T>>();
this.errorHandler = errorHandler;
}
public void Register(AsyncEventHandler<T> handler)
{
if (handler == null)
throw new ArgumentNullException(nameof(handler));
lock (this.handlers)
this.handlers.Add(handler);
}
public void Unregister(AsyncEventHandler<T> handler)
{
if (handler == null)
throw new ArgumentNullException(nameof(handler));
lock (this.handlers)
this.handlers.Remove(handler);
}
public IReadOnlyList<AsyncEventHandler<T>> Handlers
{
get
{
var temp = default(AsyncEventHandler<T>[]);
lock (this.handlers)
temp = this.handlers.ToArray();
return temp.ToList().AsReadOnly();
}
}
public async Task InvokeAsync(T ev)
{
var exceptions = new List<Exception>();
foreach (var handler in this.Handlers)
{
try
{
await handler(ev).ConfigureAwait(false);
if (ev.Handled)
break;
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
if (exceptions.Any())
this.errorHandler?.Invoke(this.name, new AggregateException(exceptions));
}
}

Return a Task from a method with type void

I would like to create a task to run serial commands on. At this time I do not need to return anything from the method that is doing the work. This will probably change later, but I am now curious as to how this.
This is what I have. I would like to use a separate method for the task instead of creating an anonymous action. I have tried returning void, with the result of "void can not be explicitly converted to a Task". I have also tried. Task<void>. The Last thing I have tried is returning a Task, but I receive, error "Not all Code paths return a value" and "Can not implicily convert void to type task"
In the pass I have used a Thread to accomplish this, but I'd like to use Tasks this time around.
internal class Hardware
{
private EventHandler<SequenceDoneEventArgs> SequenceDone;
private List<Step> Steps;
private System.IO.Ports.SerialPort comport = null;
private Task SequenceTask;
private CancellationTokenSource RequestStopSource;
private CancellationToken RequestStopToken;
private void Initialize()
{
comport = new System.IO.Ports.SerialPort("COM2", 115200, System.IO.Ports.Parity.None,8);
comport.DataReceived += Comport_DataReceived;
}
public async void RunSequence()
{
if (comport == null)
{
Initialize();
}
if (!comport.IsOpen)
{
comport.Open();
}
RequestStopSource = new CancellationTokenSource();
RequestStopToken = RequestStopSource.Token;
SequenceTask = await Task.Run(() => { doSequence(); });
}
private Task doSequence()
{
//** Run Sequence stuff here
}
}
ETA:
In the end this is my the complete solution
internal class Hardware
{
private EventHandler<SequenceDoneEventArgs> SequenceDone;
private List<Step> Steps;
private System.IO.Ports.SerialPort comport = null;
private Task SequenceTask;
private CancellationTokenSource RequestStopSource;
private CancellationToken RequestStopToken;
private void Initialize()
{
comport = new System.IO.Ports.SerialPort("COM2", 115200, System.IO.Ports.Parity.None,8);
comport.DataReceived += Comport_DataReceived;
}
public async void RunSequence()
{
if (comport == null)
{
Initialize();
}
if (!comport.IsOpen)
{
comport.Open();
}
RequestStopSource = new CancellationTokenSource();
RequestStopToken = RequestStopSource.Token;
SequenceTask = await Task.Factory.StartNew(async () => { await doSequence(); });
}
private Task doSequence()
{
//** Run Sequence stuff here
//return null;
return Task.CompletedTask;
}
}
Just mark doSequence as async (assuming it uses await):
private async Task doSequence()
Also, it's a good idea to return this Task in the delegate you pass to Task.Run:
SequenceTask = await Task.Run(() => doSequence());
I would like to create a task to run serial commands on.
This leads me to believe that using async and Task may not be the best solution for your scenario. I suggest you look into TPL Dataflow.
SequenceTask = await Task.Factory.StartNew(async() => { await doSequence(); });
Also your RunSequence() should return Task instead of void.
Actually if you await the Task this should result in the same:
SequenceTask = await doSequence();

How do I await events in C#?

I am creating a class that has a series of events, one of them being GameShuttingDown. When this event is fired, I need to invoke the event handler. The point of this event is to notify users the game is shutting down and they need to save their data. The saves are awaitable, and events are not. So when the handler gets called, the game shuts down before the awaited handlers can complete.
public event EventHandler<EventArgs> GameShuttingDown;
public virtual async Task ShutdownGame()
{
await this.NotifyGameShuttingDown();
await this.SaveWorlds();
this.NotifyGameShutDown();
}
private async Task SaveWorlds()
{
foreach (DefaultWorld world in this.Worlds)
{
await this.worldService.SaveWorld(world);
}
}
protected virtual void NotifyGameShuttingDown()
{
var handler = this.GameShuttingDown;
if (handler == null)
{
return;
}
handler(this, new EventArgs());
}
Event registration
// The game gets shut down before this completes because of the nature of how events work
DefaultGame.GameShuttingDown += async (sender, args) => await this.repo.Save(blah);
I understand that the signature for events are void EventName and so making it async is basically fire and forget. My engine makes heavy use of eventing to notify 3rd party developers (and multiple internal components) that events are taking place within the engine and letting them react to them.
Is there a good route to go down to replace eventing with something asynchronous based that I can use? I'm not sure if I should be using BeginShutdownGame and EndShutdownGame with callbacks, but that's a pain because then only the calling source can pass a callback, and not any 3rd party stuff that plugs in to the engine, which is what I am getting with events. If the server calls game.ShutdownGame(), there's no way for engine plugins and or other components within the engine to pass along their callbacks, unless I wire up some kind of registration method, keeping a collection of callbacks.
Any advice on what the preferred/recommended route to go down with this would be greatly appreciated! I have looked around and for the most part what I've seen is using the Begin/End approach which I don't think will satisfy what I'm wanting to do.
Edit
Another option I'm considering is using a registration method, that takes an awaitable callback. I iterate over all of the callbacks, grab their Task and await with a WhenAll.
private List<Func<Task>> ShutdownCallbacks = new List<Func<Task>>();
public void RegisterShutdownCallback(Func<Task> callback)
{
this.ShutdownCallbacks.Add(callback);
}
public async Task Shutdown()
{
var callbackTasks = new List<Task>();
foreach(var callback in this.ShutdownCallbacks)
{
callbackTasks.Add(callback());
}
await Task.WhenAll(callbackTasks);
}
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()).
Peter's example is great, I've just simplified it a little using LINQ and extensions:
public static class AsynchronousEventExtensions
{
public static Task Raise<TSource, TEventArgs>(this Func<TSource, TEventArgs, Task> handlers, TSource source, TEventArgs args)
where TEventArgs : EventArgs
{
if (handlers != null)
{
return Task.WhenAll(handlers.GetInvocationList()
.OfType<Func<TSource, TEventArgs, Task>>()
.Select(h => h(source, args)));
}
return Task.CompletedTask;
}
}
It may be a good idea to add a timeout. To raise the event call Raise extension:
public event Func<A, EventArgs, Task> Shutdown;
private async Task SomeMethod()
{
...
await Shutdown.Raise(this, EventArgs.Empty);
...
}
But you have to be aware that, unlike synchronous evens, this implementation calls handlers concurrently. It can be an issue if handlers have to be executed strictly consecutively what they are often do, e.g. a next handler depends on results of the previous one:
someInstance.Shutdown += OnShutdown1;
someInstance.Shutdown += OnShutdown2;
...
private async Task OnShutdown1(SomeClass source, MyEventArgs args)
{
if (!args.IsProcessed)
{
// An operation
await Task.Delay(123);
args.IsProcessed = true;
}
}
private async Task OnShutdown2(SomeClass source, MyEventArgs args)
{
// OnShutdown2 will start execution the moment OnShutdown1 hits await
// and will proceed to the operation, which is not the desired behavior.
// Or it can be just a concurrent DB query using the same connection
// which can result in an exception thrown base on the provider
// and connection string options
if (!args.IsProcessed)
{
// An operation
await Task.Delay(123);
args.IsProcessed = true;
}
}
You'd better change the extension method to call handlers consecutively:
public static class AsynchronousEventExtensions
{
public static async Task Raise<TSource, TEventArgs>(this Func<TSource, TEventArgs, Task> handlers, TSource source, TEventArgs args)
where TEventArgs : EventArgs
{
if (handlers != null)
{
foreach (Func<TSource, TEventArgs, Task> handler in handlers.GetInvocationList())
{
await handler(source, args);
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
namespace Example
{
// delegate as alternative standard EventHandler
public delegate Task AsyncEventHandler<TEventArgs>(object sender, TEventArgs e, CancellationToken token);
public class ExampleObject
{
// use as regular event field
public event AsyncEventHandler<EventArgs> AsyncEvent;
// invoke using the extension method
public async Task InvokeEventAsync(CancellationToken token) {
await this.AsyncEvent.InvokeAsync(this, EventArgs.Empty, token);
}
// subscribe (add a listener) with regular syntax
public static async Task UsageAsync() {
var item = new ExampleObject();
item.AsyncEvent += (sender, e, token) => Task.CompletedTask;
await item.InvokeEventAsync(CancellationToken.None);
}
}
public static class AsynEventHandlerExtensions
{
// invoke a async event (with null-checking)
public static async Task InvokeAsync<TEventArgs>(this AsyncEventHandler<TEventArgs> handler, object sender, TEventArgs args, CancellationToken token) {
var delegates = handler?.GetInvocationList();
if (delegates?.Length > 0) {
var tasks = delegates
.Cast<AsyncEventHandler<TEventArgs>>()
.Select(e => e.Invoke(sender, args, token));
await Task.WhenAll(tasks);
}
}
}
}
internal static class EventExtensions
{
public static void InvokeAsync<TEventArgs>(this EventHandler<TEventArgs> #event, object sender,
TEventArgs args, AsyncCallback ar, object userObject = null)
where TEventArgs : class
{
var listeners = #event.GetInvocationList();
foreach (var t in listeners)
{
var handler = (EventHandler<TEventArgs>) t;
handler.BeginInvoke(sender, args, ar, userObject);
}
}
}
example:
public event EventHandler<CodeGenEventArgs> CodeGenClick;
private void CodeGenClickAsync(CodeGenEventArgs args)
{
CodeGenClick.InvokeAsync(this, args, ar =>
{
InvokeUI(() =>
{
if (args.Code.IsNotNullOrEmpty())
{
var oldValue = (string) gv.GetRowCellValue(gv.FocusedRowHandle, nameof(License.Code));
if (oldValue != args.Code)
gv.SetRowCellValue(gv.FocusedRowHandle, nameof(License.Code), args.Code);
}
});
});
}
Note: This is async so the event handler may compromise the UI thread. The event handler (subscriber) should do no UI-work.
It wouldn't make much sense otherwise.
declare your event in your event provider:
public event EventHandler DoSomething;
Invoke event your provider:
DoSomething.InvokeAsync(new MyEventArgs(), this, ar => { callback called when finished (synchronize UI when needed here!) }, null);
subscribe the event by client as you would normally do
It's true, events are inherently un-awaitable so you'll have to work around it.
One solution I have used in the past is using a semaphore to wait for all entries in it to be released. In my situation I only had one subscribed event so I could hardcode it as new SemaphoreSlim(0, 1) but in your case you might want to override the getter/setter for your event and keep a counter of how many subscribers there are so you can dynamically set the max amount of simultaneous threads.
Afterwards you pass a semaphore entry to each of the subscribers and let them do their thing until SemaphoreSlim.CurrentCount == amountOfSubscribers (aka: all spots have been freed).
This would essentially block your program until all event subscribers have finished.
You might also want to consider providing an event à la GameShutDownFinished for your subscribers, which they have to call when they're done with their end-of-game task. Combined with the SemaphoreSlim.Release(int) overload you can now clear up all semaphore entries and simply use Semaphore.Wait() to block the thread. Instead of having to check whether or not all entries have been cleared you now wait until one spot has been freed (but there should only one moment where all spots are freed at once).
I know that the op was asking specifically about using async and tasks for this, but here is an alternative that means the handlers do not need to return a value. The code is based on Peter Duniho's example. First the equivalent class A (squashed up a bit to fit) :-
class A
{
public delegate void ShutdownEventHandler(EventArgs e);
public event ShutdownEventHandler ShutdownEvent;
public void OnShutdownEvent(EventArgs e)
{
ShutdownEventHandler handler = ShutdownEvent;
if (handler == null) { return; }
Delegate[] invocationList = handler.GetInvocationList();
Parallel.ForEach<Delegate>(invocationList,
(hndler) => { ((ShutdownEventHandler)hndler)(e); });
}
}
A simple console application to show its use...
using System;
using System.Threading;
using System.Threading.Tasks;
...
class Program
{
static void Main(string[] args)
{
A a = new A();
a.ShutdownEvent += Handler1;
a.ShutdownEvent += Handler2;
a.ShutdownEvent += Handler3;
a.OnShutdownEvent(new EventArgs());
Console.WriteLine("Handlers should all be done now.");
Console.ReadKey();
}
static void handlerCore( int id, int offset, int num )
{
Console.WriteLine("Starting shutdown handler #{0}", id);
int step = 200;
Thread.Sleep(offset);
for( int i = 0; i < num; i += step)
{
Thread.Sleep(step);
Console.WriteLine("...Handler #{0} working - {1}/{2}", id, i, num);
}
Console.WriteLine("Done with shutdown handler #{0}", id);
}
static void Handler1(EventArgs e) { handlerCore(1, 7, 5000); }
static void Handler2(EventArgs e) { handlerCore(2, 5, 3000); }
static void Handler3(EventArgs e) { handlerCore(3, 3, 1000); }
}
I hope that this is useful to someone.
If you need to await a standard .net event handler you can't do that, because it's void.
But you can create an async event system to handle that:
public delegate Task AsyncEventHandler(AsyncEventArgs e);
public class AsyncEventArgs : System.EventArgs
{
public bool Handled { get; set; }
}
public class AsyncEvent
{
private string name;
private List<AsyncEventHandler> handlers;
private Action<string, Exception> errorHandler;
public AsyncEvent(string name, Action<string, Exception> errorHandler)
{
this.name = name;
this.handlers = new List<AsyncEventHandler>();
this.errorHandler = errorHandler;
}
public void Register(AsyncEventHandler handler)
{
if (handler == null)
throw new ArgumentNullException(nameof(handler));
lock (this.handlers)
this.handlers.Add(handler);
}
public void Unregister(AsyncEventHandler handler)
{
if (handler == null)
throw new ArgumentNullException(nameof(handler));
lock (this.handlers)
this.handlers.Remove(handler);
}
public IReadOnlyList<AsyncEventHandler> Handlers
{
get
{
var temp = default(AsyncEventHandler[]);
lock (this.handlers)
temp = this.handlers.ToArray();
return temp.ToList().AsReadOnly();
}
}
public async Task InvokeAsync()
{
var ev = new AsyncEventArgs();
var exceptions = new List<Exception>();
foreach (var handler in this.Handlers)
{
try
{
await handler(ev).ConfigureAwait(false);
if (ev.Handled)
break;
}
catch(Exception ex)
{
exceptions.Add(ex);
}
}
if (exceptions.Any())
this.errorHandler?.Invoke(this.name, new AggregateException(exceptions));
}
}
And you can declare now your async events:
public class MyGame
{
private AsyncEvent _gameShuttingDown;
public event AsyncEventHandler GameShuttingDown
{
add => this._gameShuttingDown.Register(value);
remove => this._gameShuttingDown.Unregister(value);
}
void ErrorHandler(string name, Exception ex)
{
// handle event error.
}
public MyGame()
{
this._gameShuttingDown = new AsyncEvent("GAME_SHUTTING_DOWN", this.ErrorHandler);.
}
}
And invoke your async event using:
internal async Task NotifyGameShuttingDownAsync()
{
await this._gameShuttingDown.InvokeAsync().ConfigureAwait(false);
}
Generic version:
public delegate Task AsyncEventHandler<in T>(T e) where T : AsyncEventArgs;
public class AsyncEvent<T> where T : AsyncEventArgs
{
private string name;
private List<AsyncEventHandler<T>> handlers;
private Action<string, Exception> errorHandler;
public AsyncEvent(string name, Action<string, Exception> errorHandler)
{
this.name = name;
this.handlers = new List<AsyncEventHandler<T>>();
this.errorHandler = errorHandler;
}
public void Register(AsyncEventHandler<T> handler)
{
if (handler == null)
throw new ArgumentNullException(nameof(handler));
lock (this.handlers)
this.handlers.Add(handler);
}
public void Unregister(AsyncEventHandler<T> handler)
{
if (handler == null)
throw new ArgumentNullException(nameof(handler));
lock (this.handlers)
this.handlers.Remove(handler);
}
public IReadOnlyList<AsyncEventHandler<T>> Handlers
{
get
{
var temp = default(AsyncEventHandler<T>[]);
lock (this.handlers)
temp = this.handlers.ToArray();
return temp.ToList().AsReadOnly();
}
}
public async Task InvokeAsync(T ev)
{
var exceptions = new List<Exception>();
foreach (var handler in this.Handlers)
{
try
{
await handler(ev).ConfigureAwait(false);
if (ev.Handled)
break;
}
catch (Exception ex)
{
exceptions.Add(ex);
}
}
if (exceptions.Any())
this.errorHandler?.Invoke(this.name, new AggregateException(exceptions));
}
}

Categories

Resources