Async call on GUI event - c#

I've an event on an UserControl(winform) that I'm listening:
public void OnSomething(SomeEventArgs args){
SomeResult result = ?await? _businessObject.AsyncCall();
ApplySomethingOnTheGuiBasedOnTheResult(result);
}
_businessObject.AsyncCall() has the following signature:
public async Task<SomeResult> AsyncCall();
How can I call this async method while:
Not blocking the GUI thread
Being able to call the ApplySomethingOnTheGuiBasedOnTheResult after my async method has been called
Being able to call the ApplySomethingOnTheGuiBasedOnTheResult in the correct(GUI) thread.
What I tried:
public void OnSomething(SomeEventArgs args){
SomeResult result = await _businessObject.AsyncCall();
ApplySomethingOnTheGuiBasedOnTheResult(result);
}
--> Doesn't compile since my event handler isn't async.
public void OnSomething(SomeEventArgs args){
SomeResult result = _businessObject.AsyncCall().Result;
ApplySomethingOnTheGuiBasedOnTheResult(result);
}
--> The application freeze
public void OnSomething(SomeEventArgs args){
_businessObject.AsyncCall().ContinueWith(t=>{
if(InvokeRequired){
Invoke(new Action(()=>ApplySomethingOnTheGuiBasedOnTheResult(t.Result)));
}
});
}
--> Works, but I was hopping that there was a nicer way to do it with the async/await.

You simply need to mark your handler as async:
public async void OnSomething(SomeEventArgs args)
{
SomeResult result = await _businessObject.AsyncCall();
ApplySomethingOnTheGuiBasedOnTheResult(result);
}
I know it's not recommended to use asyc with return type void (normally it should be Task or Task<sometype>), but if the event requires that signature, it's the only way.
This way the control flow is returned to the caller at the await statement. When the AsyncCall has finished, execution is eventually resumed after the await on the UI thread.

Related

Wait for a specific result from Task then run another method in C#

I have a WPF app running on .net 6 and an external device connected to it.
Initializing the device sometimes fails and I don't want to hold the UI thread trying to initialize it.
I want to run the following method (_device.Init()) in an async fashion and when it returns true, run Start() method.
edit: run it until it returns true from the _device.Init() method, not true for finishing the task
Is there a built-in functionality to do it with tasks? or any other "best practice" way?
Thank you :)
SomeDevice _device = new();
public async void Init()
{
// some other code
while (Task.Run(() => _device.Init()).Result == false)
{
}
Start();
}
public void Start()
{
// some other code
Application.Current.Dispatcher.BeginInvoke(new Action(() =>
{
_device.Start();
}));
}
Instead of getting the Result of the Task (which may block the UI thread) you should await the Task:
public async void Init()
{
// some other code
while (!await Task.Run(() => _device.Init()))
{
}
Start();
}
The method should also be awaitable and be awaited when called, e.g. in an async Loaded event handler:
public async Task Init()
{
// some other code
while (!await Task.Run(() => _device.Init()))
{
}
Start();
}
...
await Init();
public async void Init()
{
var task = _device.Init();
//do work here
await task;
Start();
}
Should do the trick, it'll do the work and then wait for the task to complete before going to Start();
If you want to simply wait for init to finish and then run start it's even simpler with
await _device.Init().ContinueWith((x) => { Start();})

await and event handler

It's permitted to convert and usual event handler from void to Task based and await it like below?
Something.PropertyChanged += async (o, args) => await IsButtonVisible_PropertyChanged(o, args);
Something.PropertyChanged -= async (o, args) => await IsButtonVisible_PropertyChanged(o, args);
private Task IsButtonVisible_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
if (IsSomthingEnabled)
{
return SomeService.ExecuteAsync(...);
}
return Task.CompletedTask;
}
Or do it like this?
Something.PropertyChanged += IsButtonVisible_PropertyChanged;
Something.PropertyChanged -= IsButtonVisible_PropertyChanged;
private void IsButtonVisible_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
if (IsSomthingEnabled)
{
_ = SomeService.ExecuteAsync(...);
}
}
Update:
Or this one, I know that the use Task void It should be banned, because exception it's not catched, but maybe for the case of an Eventhandler it's ok since the Eventhandler doesn't return.
Something.PropertyChanged += IsButtonVisible_PropertyChanged;
Something.PropertyChanged -= IsButtonVisible_PropertyChanged;
private async void IsButtonVisible_PropertyChanged(object sender,PropertyChangedEventArgs e)
{
if (IsSomthingEnabled)
{
await = SomeService.ExecuteAsync(...);
}
}
The syntax for asynchronous event handlers is :
Something.PropertyChanged += IsButtonVisible_PropertyChanged;
...
private async void IsButtonVisible_PropertyChanged(object sender,
PropertyChangedEventArgs e)
{
if (IsSomethingEnabled)
{
await SomeService.ExecuteAsync(...);
}
}
This allows awaiting asynchronous operations inside the event handler without blocking the UI thread. This can't be used to await for an event in some other method though.
Awaiting a single event
If you want some other code to await for an event to complete you need a TaskCompletionSource. This is explained in Tasks and the Event-based Asynchronous Pattern (EAP).
public Task<string> OnPropChangeAsync(Something x)
{
var options=TaskCreationOptions.RunContinuationsAsynchronously;
var tcs = new TaskCompletionSource<string>(options);
x.OnPropertyChanged += onChanged;
return tcs.Task;
void onChanged(object sender,PropertyChangedEventArgs e)
{
tcs.TrySetResult(e.PropertyName);
x.OnPropertyChanged -= onChanged;
}
}
....
async Task MyAsyncMethod()
{
var sth=new Something();
....
var propName=await OnPropertyChangeAsync(sth);
if (propName=="Enabled" && IsSomethingEnabled)
{
await SomeService.ExecuteAsync(...);
}
}
This differs from the example in two places:
The event handler delegate gets unregistered after the event fires. Otherwise the delegate would remain in memory as long as Something did.
TaskCreationOptions.RunContinuationsAsynchronously ensures that any continuations will run on a separate thread. The default is to run them on the same thread that sets the result
This method will await only a single event. Calling it in a loop will create a new TCS each time, which is wasteful.
Awaiting a stream of events
It wasn't possible to easily await multiple events until IAsyncEnumerable was introduced in C# 8. With IAsyncEnumerable<T> and Channel, it's possible to create a method that will send a stream of notifications :
public IAsyncEnumerable<string> OnPropChangeAsync(Something x,CancellationToken token)
{
var channel=Channel.CreateUnbounded<string>();
//Finish on cancellation
token.Register(()=>channel.Writer.TryComplete());
x.OnPropertyChanged += onChanged;
return channel.Reader.ReadAllAsync();
async void onChanged(object sender,PropertyChangedEventArgs e)
{
channel.Writer.SendAsync(e.PropertyName);
}
}
....
async Task MyAsyncMethod(CancellationToken token)
{
var sth=new Something();
....
await foreach(var prop in OnPropertyChangeAsync(sth),token)
{
if (propName=="Enabled" && IsSomethingEnabled)
{
await SomeService.ExecuteAsync(...);
}
}
}
In this case, only one event handler is needed. Every time an event occurs the property named is pushed to the Channel. Channel.Reader.ReadAllAsync() is used to return an IAsyncEnumerable<string> that can be used to loop asynchronously. The loop will keep running until the CancellationToken is signaled, in which case the writer will go into the Completed state and the IAsyncEnumerable<T> will terminate.
Quoting from Microsoft's article Async/Await - Best Practices in Asynchronous Programming, and specifically from the Avoid async void section:
Void-returning async methods have a specific purpose: to make asynchronous event handlers possible. [...] Event handlers naturally return void, so async methods return void so that you can have an asynchronous event handler.
Based on this, your third approach is the correct one:
private async void IsButtonVisible_PropertyChanged(object sender,
PropertyChangedEventArgs e)
{
if (IsSomethingEnabled)
{
await SomeService.ExecuteAsync();
}
}
Your first approach (+= async (o, args) => await) is technically equivalent, but it's not recommended because it is idiomatic and may cause confusion to future maintainers.
Your second approach (_ = SomeService.ExecuteAsync() launches the asynchronous operation in a fire-and-forget fashion, which is rarely a good idea because your application completely loses track of this task. It also elides async and await, which opens another can of worms.
the syntax for an async Event Handler is
async void handler(object sender,EventArgs args){}
and as Events don't return there is nothing to await for, so waiting for them is pointless
however if you need a response from an event then you can use the EventsArgs class to provide the response, eg
class FeedbackEventArgs:EventArgs
{
event EventHandler Completed;
Complete(){
this.Completed(this,EventArgs.Empty);
}
}
then you can use it as
event EventHandler<FeedbackEventArgs> myFeedbackEvent;
args = new FeedbackEventArgs();
args.Completed += OnCompleted;
this.myFeedbackEvent(this,args)
note if your handler is not async then you can assume that you code was paused while the event occurred, in which case you can just read a property from the eventArg rather than having to trigger an event
class FeedbackEventArgs:EventArgs
{
int result{get;set;}
}
event EventHandler<FeedbackEventArgs> myFeedbackEvent;
this.myFeedbackEvent(this,args)
args.result //this will be the result set in the sync handler
as noted by #Panagiotis this is a conceptual example not a working example

How to await an async method which isn't expected to return

I'm making a TCP server. I'm using async/await to handle the multi-threading. The methods I am using for listening to incoming clients and there subsequent messages look a bit like this:
private static async Task Listener()
{
while (Online)
{
TcpClient socket = await tcpListener.AcceptTcpClientAsync();
OnReceivedTcpClient(socket);
}
}
As you can tell, this method isn't expected to return anytime soon. The question I have is regarding how I should call this listener method. Currently the way I'm doing it is this:
(Console App)
In Program.cs within Main, I call Server.Start()
static void Main(string[] args)
{
Console.Title = "Server (Prototype)";
Server.Start(100, 26950);
ConsoleKeyInfo input;
do
{
input = Console.ReadKey();
// check input and do stuff
}
}
while (input.Key != ConsoleKey.C);
}
Server.Start initilises some values and then calls an event which in turn calls the listener
private static event EventHandler<EventArgs> StartEvent;
private static void OnStartEvent() => StartEvent?.Invoke(null, new EventArgs());
public static void Start(int maxClients, int port)
{
Stop();
Console.WriteLine("Starting server...");
Init(maxClients, port);
OnStartEvent();
}
private async void ServerOnStartEvent(object sender, EventArgs e)
{
Online = true;
Console.WriteLine($"Server started on port {Port}");
await Listener();
}
If I had called await Listener(); inside of Server.Start then that method would need the async keyword, and it would have to either return void (Which I know is not an ideal design) or return a Task which then means I would have to call _ = Server.Start() inside program.cs (Which also is not great design).
So my question is, is my solution a good way of awaiting an async Task method and are there better ways to go about it?
The way I usually deal with this is to also add a Stop-method. So Start launches the task and saves it in a field. the stop method requests the task to stop (by whatever means), and returns the task that was stored.
So the caller can await the result from the stop method, and once the task completes, the caller can be sure any resources are cleaned up etc.
A variant would be to let the Start method return something like a IAsyncDisposable, that could allow the using-statement to automatically stop and wait for cleanup when going out of scope.
Example:
public class MyClass
volatile bool stopRequested; // or use CancellationTokenSource
Task task;
public void Start() => task = Task.Run(DoWork); // Should probably make this "longRunning"
public void DoWork(){
while(!stopRequested){
// Do something that take a limited amount of time.
}
// Do cleanup
}
public Task Stop(){
stopRequested = true;
return task;
}

Handling moment when async CoreDispatcher work completed or cancelled

For example, I need to use CoreDispatcher for refreshing MVVM properties in the UI Thread.
private void ButtonClick(object sender, RoutedEventArgs e)
{
//Code not compile without keyword async
var dispatcherResult = this.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
//This method contains awaitable code
await _scanner.ScanAsync();
}
);
dispatcherResult.Completed = new AsyncActionCompletedHandler(TaskInitializationCompleted);
}
private void TaskInitializationCompleted (IAsyncAction action, AsyncStatus status )
{
//Do something...
}
I am expect, then TaskInitializationCompleted handler will fire AFTER ScanAsync method completed, but it fire immediatly after Dispatcher.RunAsync method started and also BEFORE then ScanAsync was completed.
How I can check to really handle async Dispatcher work completed or cancelled?
Instead of registering to the Completed event, you can await RunAsync (Because DispatcherOperation is an awaitable) which will guarantee any code runs only after completion the invocations completion:
private async void ButtonClick(object sender, RoutedEventArgs e)
{
var dispatcherResult = await this.Dispatcher
.RunAsync(CoreDispatcherPriority.Normal,
async () =>
{
await _scanner.ScanAsync();
});
// Do something after `RunAsync` completed
}

Is it possible to have async methods as callbacks to eventhandlers in c#?

My design is illustrated by below example. Having a while true loop doing something and notifying by an event that it has done something to all subscribers. My application should not continue its execution before its done notifying all subscribers, where this works as long as someone do not put a async void on the callback.
If someone put a async void on the callback to await some task, then my loop can continue before the callback is completed. What other designs can I do to avoid this situation.
Its 3th party plugins that register themeself and subscribe to the event, so I have no control over if they put a async void. Understandable I cant do Task callbacks for the EventHandler, so what alternatives do I have with .net 4.5.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace ConsoleApplication4
{
public class Test
{
public event EventHandler Event;
public void DoneSomething()
{
if (Event != null)
Event(this,EventArgs.Empty);
}
}
class Program
{
static void Main(string[] args)
{
var test = new Test();
test.Event += test_Event;
test.Event +=test_Event2;
while(true)
{
test.DoneSomething();
Thread.Sleep(1000);
}
}
private static void test_Event2(object sender, EventArgs e)
{
Console.WriteLine("delegate 2");
}
static async void test_Event(object sender, EventArgs e)
{
Console.WriteLine("Del1gate 1");
await Task.Delay(5000);
Console.WriteLine("5000 ms later");
}
}
}
If someone put a async void on the callback to await some task, then my loop can continue before the callback is completed. What other designs can I do to avoid this situation.
There is really no way to avoid this. Even if you were to somehow "know" that the subscriber wasn't implemented via async/await, you still couldn't guarantee that the caller didn't build some form of asynchronous "operation" in place.
For example, a completely normal void method could put all of its work into a Task.Run call.
My application should not continue its execution before its done notifying all subscribers
Your current version does follow this contract. You're notifying the subscribers synchronously - if a subscriber does something asynchronously in response to that notification, that is something outside of your control.
Understandable I cant do Task callbacks for the EventHandler, so what alternatives do I have with .net 4.5.
Note that this is actually possible. For example, you can rewrite your above as:
public class Program
{
public static void Main()
{
var test = new Test();
test.Event += test_Event;
test.Event +=test_Event2;
test.DoneSomethingAsync().Wait();
}
}
public delegate Task CustomEvent(object sender, EventArgs e);
private static Task test_Event2(object sender, EventArgs e)
{
Console.WriteLine("delegate 2");
return Task.FromResult(false);
}
static async Task test_Event(object sender, EventArgs e)
{
Console.WriteLine("Del1gate 1");
await Task.Delay(5000);
Console.WriteLine("5000 ms later");
}
public class Test
{
public event CustomEvent Event;
public async Task DoneSomethingAsync()
{
var handler = this.Event;
if (handler != null)
{
var tasks = handler.GetInvocationList().Cast<CustomEvent>().Select(s => s(this, EventArgs.Empty));
await Task.WhenAll(tasks);
}
}
}
You can also rewrite this using event add/remove, as suggested by svick:
public class Test
{
private List<CustomEvent> events = new List<CustomEvent>();
public event CustomEvent Event
{
add { lock(events) events.Add(value); }
remove { lock(events) events.Remove(value); }
}
public async Task DoneSomething()
{
List<CustomEvent> handlers;
lock(events)
handlers = this.events.ToList(); // Cache this
var tasks = handlers.Select(s => s(this, EventArgs.Empty));
await Task.WhenAll(tasks);
}
}
My application should not continue its execution before its done notifying all subscribers, where this works as long as someone do not put a async void on the callback.
I have a blog entry on designing for async event handlers. It is possible to use Task-returning delegates or to wrap an existing SynchronizationContext within your own (which would allow you to detect and wait for async void handlers).
However, I recommend you use "deferrals", which are objects designed specifically to solve this problem for Windows Store applications. A simple DeferralManager is available in my AsyncEx library.
Your event args can define a GetDeferral method as such:
public class MyEventArgs : EventArgs
{
private readonly DeferralManager deferrals = new DeferralManager();
... // Your own constructors and properties.
public IDisposable GetDeferral()
{
return deferrals.GetDeferral();
}
internal Task WaitForDeferralsAsync()
{
return deferrals.SignalAndWaitAsync();
}
}
And you can raise an event and (asynchronously) wait for all asynchronous handlers to complete like this:
private Task RaiseMyEventAsync()
{
var handler = MyEvent;
if (handler == null)
return Task.FromResult<object>(null); // or TaskConstants.Completed
var args = new MyEventArgs(...);
handler(args);
return args.WaitForDeferralsAsync();
}
The benefit of the "deferral" pattern is that it is well-established in the Windows Store APIs, so it's likely to be recognized by end users.

Categories