The common description of why async void is part of C# is for event handlers. For example:
private async void button_Click(object sender, RoutedEventArgs e)
{
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync("http://example.com");
var content = await response.Content.ReadAsStringAsync();
this.textBox.Text = content;
}
}
I find this reason unsatisfying, as this kind of event handler can be written without async void like this:
private void button_Click(object sender, RoutedEventArgs e)
{
button_ClickAsync().ForgetTask();
}
private async Task button_ClickAsync()
{
using (var httpClient = new HttpClient())
{
var response = await httpClient.GetAsync("http://example.com");
var content = await response.Content.ReadAsStringAsync();
this.textBox.Text = content;
}
}
static class TaskExtensions { public static void ForgetTask(this Task task) { } }
Why isn't the latter good enough? Why is async void a necessary part of C#? What problem can't be solved without async void?
As you showed yourself, it's not necessary. You can write a functionally identical program to one that uses it without using it. It's useful insofar as there simply are times where you really do want to create an async method that doesn't expose any way of observing the result, such as in the situation you mentioned. Could they have designed the feature in such a way that users could accomplish that another way, yes, you showed one possible way, the C# language designers choose another.
Exceptions from an unawaited async Task method will be unobserved, firing the TaskScheduler.UnobservedTaskException event. For example:
static void Main()
{
TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs args) => { Console.WriteLine(args.Exception.InnerException.Message + " unobserved"); };
try
{
ThrowExceptionInAsyncTask();
Console.WriteLine("ThrowExceptionInAsyncTask not caught");
}
catch (Exception)
{
Console.WriteLine("ThrowExceptionInAsyncTask caught");
}
GC.Collect();
try
{
ThrowExceptionInAsyncVoid();
Console.WriteLine("ThrowExceptionInAsyncVoid not caught");
}
catch (Exception)
{
Console.WriteLine("ThrowExceptionInAsyncVoid caught");
}
GC.Collect();
}
static async Task ThrowExceptionInAsyncTask()
{
throw new InvalidOperationException("ThrowExceptionInAsyncTask");
}
static async void ThrowExceptionInAsyncVoid()
{
throw new InvalidOperationException("ThrowExceptionInAsyncVoid");
}
Produces:
ThrowExceptionInAsyncTask not caught
ThrowExceptionInAsyncVoid not caught
ThrowExceptionInAsyncTask unobserved
It is used in two cases as in my own knowledge:
In event handlers. Because event handlers cannot have a return type.
In a method intended to have no return type, and not awaited.
What you did here is a good workaround, but think of it as flexibility in language to make life easier for developers and simplify language syntax. Same example like yours is: Why System.Linq exists in C#?! Why we can use syntax like myIntArray.Max() while we can iterate through the array and find the maximum value!
That doesn't mean there is no other reason, but I'm sharing my thoughts and I hope that helps.
Related
I have a legacy Windows Forms application that I am working on, I made some changes to the http client, I wanted to make it a singleton so that it could be reused throughout the application. It seems to be causing a deadlock.
I am going to paste all the code that I believe is involved below:
This is the calling code where the UI gets frozen, it never unfreezes.
private async void lbGroup_SelectedIndexChanged_1(object sender, EventArgs e)
{
int groupId = this.lbGroup.SelectedIndex + 1;
await LoadStores(groupId);
//The code below freezes the application
this.lbStore.DataSource = _stores;
this.txtSearch.Enabled = true;
this.lbStore.Enabled = true;
}
This is the LoadStores Method where the httpClient is used:
private async Task LoadStores(int group)
{
try
{
HttpResponseMessage res = await _httpClient.GetAsync("api/GetStoresByGroup/" + group.ToString());
res.EnsureSuccessStatusCode();
if (res.IsSuccessStatusCode)
{
var serializedStores = await res.Content.ReadAsStringAsync();
_stores = JsonConvert.DeserializeObject<IEnumerable<Store>>(serializedStores).Select(s => s.StoreName).ToList();
res.Content.Dispose();
}
}
catch (Exception ex)
{
ErrorLogger.LogError("Installation", $"Error getting stores list: {ex.Message}");
}
}
This is the Http Singleton Class:
public static class HttpClientSingleton
{
private static readonly HttpClient _instance;
static HttpClientSingleton()
{
_instance = new HttpClient();
_instance.BaseAddress = new Uri("https://www.i-city.co.za/");
_instance.DefaultRequestHeaders.Accept.Clear();
_instance.DefaultRequestHeaders.Accept.Add(new System.Net.Http.Headers.MediaTypeWithQualityHeaderValue("application/json"));
}
public static HttpClient Instance
{
get
{
return _instance;
}
}
}
This is the form constructor where the HttpClient gets initiliazed:
public partial class frmInstallationHelper : Form
{
private static string _configDir;
private static string _localConfigDir;
private static int _storeID;
private static Activation _activation;
private static HttpClient _httpClient = HttpClientSingleton.Instance;
private static IEnumerable<string> _stores;
private static IEnumerable<string> _franchisees;
private int _smsCounter;
If I wrap the http request in a using statement inside of the LoadStores method, the app runs fine, but I don't want to dispose of the http Client as that defeats the purpose of making it a singleton.
Update: Problem Found
After following #MongZhu's lead I replicated the program and confirmed that none of the above code was actually causing the deadlock. It was caused by another method that was triggered by the lbStore list Box onSelectChange event displayd below:
private void lbStore_SelectedIndexChanged_1(object sender, EventArgs e)
{
string store = this.lbStore.GetItemText(this.lbStore.SelectedItem);
LoadFranchisees(store).Wait();
this.lbFranchisees.DataSource = _franchisees;
}
The way I solved the problem was by changing it to look as follows:
private async void lbStore_SelectedIndexChanged_1(object sender, EventArgs e)
{
string store = this.lbStore.GetItemText(this.lbStore.SelectedItem);
await LoadFranchisees(store);
this.lbFranchisees.DataSource = _franchisees;
}
I was busy changing all the .wait() methods to async / await, and I must have forgotten this one.
The deadlock arises because you used Wait in a method which was triggered by an async opertaion. Unfortunately it was masked very good by the apparent hanging in the line of the initialization of the DataSource. But this initialization triggered the SelectedIndexChanged of the listbox which had the evil Wait call in it. Making this method async and await the result will evaporate the deadlock.
private async void lbStore_SelectedIndexChanged_1(object sender, EventArgs e)
{
string store = this.lbStore.GetItemText(this.lbStore.SelectedItem);
_franchisees = await LoadFranchisees(store);
this.lbFranchisees.DataSource = _franchisees;
}
I would suggest to return the stores directly from the method instead of using a class variable as transmitter. This way you would also avoid race conditions (to which methods that use class variables are very much prone) If you need it further you could store the returning value inside the _stores variable. But a loading method should rather return the results instead of secretely storing it somewhere hidden from the user of this method.
private async Task<List<Store>> LoadStores(int group)
{
try
{
HttpResponseMessage res = await _httpClient.GetAsync("api/GetStoresByGroup/" + group.ToString()))
res.EnsureSuccessStatusCode();
if (res.IsSuccessStatusCode)
{
var serializedStores = await res.Content.ReadAsStringAsync();
res.Content.Dispose();
return JsonConvert.DeserializeObject<IEnumerable<Store>>(serializedStores).Select(s => s.StoreName).ToList();
}
}
catch (Exception ex)
{
ErrorLogger.LogError("Installation", $"Error getting stores list: {ex.Message}");
}
}
You can await the result in the event:
private async void lbGroup_SelectedIndexChanged_1(object sender, EventArgs e)
{
int groupId = this.lbGroup.SelectedIndex + 1;
_stores = await LoadStores(groupId);
this.lbStore.DataSource = _stores;
this.txtSearch.Enabled = true;
this.lbStore.Enabled = true;
}
The same logic applies to the LoadFranchisees method, refactor it so that it returns the data. This makes your code much more understandable. Don't hide information from the reader of a method. It could be you in 6 Month trying to figure out what da heck you did there.... Be nice to your future self at least ;)
I have the 3 methods below in a razor file
protected override async Task OnInitializedAsync()
{
EditContext = new EditContext(_projectModel);
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
}
private async Task EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
await SetOkDisabledStatus();
}
This method is an async method and I have to await it anywhere it is been called
private async Task SetOkDisabledStatus()
{
if (EditContext.Validate())
{
OkayDisabled = null;
await JsRuntime.InvokeVoidAsync("Animate");
}
else
{
OkayDisabled = "disabled";
}
}
I am using the EditContext for validation in a Blazor server application.
I have been getting the error message on this line below in the OnInitializedAsync() method and not sure how to proceed with it.
EditContext.OnFieldChanged += EditContext_OnFieldChanged;
Error Message:
Task MyProject.EditContext_OnFieldChanged(object, FieldChangedEventArgs)'
has the wrong return type.
Expected a method with void EditContext_OnFieldChanged(object?, FieldChangedEventArgs e)
Please note that I am using sonarqube to check all my code.
You can assign an async lambda to the event handler, like this:
EditContext.OnFieldChanged +=
async (sender,args) => await EditContext_OnFieldChanged(sender,args);
But, you should be aware that the EditContext/Form will not await your task. Anything you do in that async task will be out of sync with the editcontext.
You should probably include a cancellation token in your async code as well, so that multiple changes to a field do not fire multiple validation tasks at the same time.
Async validation is hard - make sure you test every possible scenario.
Generated Blazor eventhandlers (like #onclick="...") are flexible about return type and parameters but EditContext.OnFieldChanged is not, it has a fixed delegate type.
Make the following change:
//private async Task EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
private async void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
await SetOkDisabledStatus();
StateHasChanged(); // make sure OkayDisabled takes effect
}
On another note, you can probably make OkayDisabled a boolean and use disabled="#OkayDisabled" where you need it.
Blazor makes the disabled attribute disappear when you assign it with false.
Alternative: keep the validation synchronous. That might prevent some problems as #Mister Magoo points out. And then let only the Animation run async.
private void EditContext_OnFieldChanged(object sender, FieldChangedEventArgs e)
{
SetOkDisabledStatus();
}
private void SetOkDisabledStatus()
{
if (EditContext.Validate())
{
OkayDisabled = null;
_ = JsRuntime.InvokeVoidAsync("Animate"); // no await, on purpose
}
else
{
OkayDisabled = "disabled";
}
}
StateHasChanged() should not be needed in this scenario.
This question already has answers here:
Do you have to put Task.Run in a method to make it async?
(3 answers)
Closed 1 year ago.
When I call an async method which returns void, is it the same as when I invoke it with the Task.Run method? I ask because in the doc of the FileSystemWatcher they mention the following.
Keep your event handling code as short as possible.
https://learn.microsoft.com/en-us/dotnet/api/system.io.filesystemwatcher?view=net-5.0#events-and-buffer-sizes
So I want to leave the scope of the event method very fast. Or do they mean something different?
Snippet of my code for better understanding.
private void OnCreated(object sender, FileSystemEventArgs e)
{
RunSaveWithLogger(AddLocation, e.FullPath);
}
private async void RunSaveWithLogger(Func<string, Task> func, string fullPath)
{
Edit:
After reading your answers and comments, I changed my code to this.
private void OnCreated(object sender, FileSystemEventArgs e)
{
Task.Run(() =>
{
RunSaveWithLogger(AddLocation, e.FullPath);
});
}
private async void RunSaveWithLogger(Func<string, Task> func, string fullPath)
{
try
{
await func.Invoke(fullPath);
}
catch (Exception exception)
{
_logger.LogError(exception, "");
}
}
Thanks for your time and help, I really appreciate that.
simple answer No! they are not the same. for instance, in the example below, "Task.Run" creates a new thread and so, runs every code within it in a new thread, while "async void" doesn't. (I doubt if this is the answer you are looking for though).
using System;
using System.Threading.Tasks;
class Solution
{
static void Main(string[] args)
{
async void Method1()
{
while (true)
{
}
}
Task.Run(() => {
while (true)
{
}
});
Console.WriteLine("This will print");
Method1();
Console.WriteLine("This won't");
}
}
If await can be used only by async methods, how can I call a task from MainPage()?
My code sample:
public MainPage()
{
InitializeComponent();
label.Text=await Task.Run(TaskTest); //this doesn't work
}
private async Task<string> TaskTest()
{
try
{
using (WebClient client = new WebClient())
{
return await client.DownloadStringTaskAsync("https://www.example.com/return.php");
//also tried w/ no success:
//return client.DownloadStringTaskAsync("https://www.example.com/return.php").Result;
}
}
catch (Exception)
{
throw;
}
}
Avoid async void fire-and-forget methods.
Event handlers however are the only exception to that rule.
Reference Async/Await - Best Practices in Asynchronous Programming
In this case, since you want to await the task then create and event and handler that would facilitate the desired behavior
public MainPage() {
InitializeComponent();
Downloading += OnDownloading; //subscribe to event
Downloading(this, EventArgs.Empty); //raise event to be handled
}
private event EventHandler Downloading = delegate { };
private async void OnDownloading(object sender, EventArgs args) {
//Downloading -= OnDownloading; //unsubscribe (optional)
label.Text = await TaskTest(); //this works
}
private async Task<string> TaskTest() {
try {
using (WebClient client = new WebClient()) {
return await client.DownloadStringTaskAsync("https://www.example.com/return.php");
}
} catch (Exception) {
throw;
}
}
You cannot make the Main() method asynchronous and thus, you can use the await keyword in the body of the Main() function.
A simple workaround that you can implement by editing your current code is making your function TaskTest() return void so you don't have to await it's call.
Example:
public MainPage()
{
InitializeComponent();
TaskTest();
}
private async void TaskTest()
{
try
{
using (WebClient client = new WebClient())
{
label.Text = await client.DownloadStringTaskAsync("https://www.example.com/return.php");
}
}
catch (Exception)
{
throw;
}
}
Edit
In case you have to wait for the return value of an asynchronous call without using await, you could go ahead and use a while to check whether the Task has completed or not.
Task<string> accessTokenTask = Task.Run<string>(() => MethodToGetToken());
// wait until operation is done.
while(!accessTokenTask.IsCompleted)
{
accessTokenTask.Wait():
}
// once the task completes, the runtime will step out of the while loop
// and you can access your Token in the Result
string token = accessTokenTask.Result;
Hope this answers your question.
You probably shouldn't call your Task from MainPage. I started with the Visual Studio blank page and tried to do the same thing. I found an answer suggested to use await Navigation.PushModalAsync(NewPage);, and then call the task there Task.Run(async () => { await method(); }).Wait();. It worked, but not the best way to do it.
This article on CodeProject is great to help beginners to add MVVM to the blank page project. You just need to bind the ViewModel to the MainPage, and then call your Task from the ViewModel instead.
public MainPage()
{
InitializeComponent();
this.BindingContext = new MainPageViewModel(this);
}
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.