How to wait on task that runs async method - c#

I'm writing WPF app and recently started working with await/async so the GUI thread does not perform any time consuming operations.
My problem is I want to load two collections from db asynchronously using Entity framework. I know I can't call two ToListAsync() methods on DbContext so I wanted to use tasks.
I wrote async method LoadData() that should wait on completing the LoadNotifications() and then call LoadCustomers().
But when the execution gets to await this.context.MailingDeliveryNotifications.ToListAsync(); it creates another task and somehow it doesn't care about the task.Wait() in my LoadData() method, so it calls LoadCustomers() before completing the first call on DbContext.
The code:
public async void LoadData()
{
Task task = this.LoadNotifications();
task.Wait();
await this.LoadCustomers();
}
private Task LoadNotifications()
{
return Task.Run(() => this.LoadNotificationsAsync());
}
private async void LoadNotificationsAsync()
{
List<MailingDeliveryNotification> res = await this.context.MailingDeliveryNotifications.ToListAsync();
this.Notifications = new ObservableCollection<MailingDeliveryNotification>(res);
}
private Task LoadCustomers()
{
return Task.Run(() => this.LoadNotificationsAsync());
}
private async void LoadCustomersAsync()
{
List<Customer> res = await this.context.Customers.ToListAsync();
this.Customers = new ObservableCollection<Customer>(res);
}
I know I can solve this using this code
public async void LoadData()
{
List<MailingDeliveryNotification> res = await this.context.MailingDeliveryNotifications.ToListAsync();
this.Notifications = new ObservableCollection<MailingDeliveryNotification>(res);
List<Customer> res2 = await this.context.Customers.ToListAsync();
this.Customers = new ObservableCollection<Customer>(res2);
}
but when I will need to add another collection to load from db, this method will grow to much. I want to keep my code Clean.

Simplify your code:
public async Task LoadDataAsync()
{
await LoadNotificationsAsync();
await LoadCustomersAsync();
}
private async Task LoadNotificationsAsync()
{
var res = await context.MailingDeliveryNotifications.ToListAsync();
Notifications = new ObservableCollection<MailingDeliveryNotification>(res);
}
private async Task LoadCustomersAsync()
{
var res = await context.Customers.ToListAsync();
Customers = new ObservableCollection<Customer>(res);
}
Or probably just:
public async Task LoadDataAsync()
{
Notifications = new ObservableCollection<MailingDeliveryNotification>(
await context.MailingDeliveryNotifications.ToListAsync());
Customers = new ObservableCollection<Customer>(
await context.Customers.ToListAsync());
}

Related

Task caused UI Freezing - WPF

The structure of my code is shown below:
internal async Task ButtonClickMethod()
{
await this.InitializeMethod();
}
internal async Task InitializeMethod()
{
await this.Call();
}
private async Task Call()
{
await this.CallTasks();
}
private async Task CallTasks()
{
List<Task> tasks = new List<Task>();
tasks.Add(/*time-consuming task A*/);
tasks.Add(/*time-consuming task B*/);
tasks.Add(/*time-consuming task C*/);
tasks.Add(/*time-consuming task D*/);
await Task.WhenAll(tasks);
}
Task defined as follows:
/*time-consuming task*/
internal async Task TimeConsuming()
{
// code...
await Task.Run(() =>
{
// code... A/B/C/D
});
// code...
}
Is there any problem with this code that will cause the UI to freeze?
Is the problem caused by Task.WhenAll?
Use this instead :
private async void ClickButton(object sender, RoutedEventArgs e)
{
await ThatFirstMethod(any parameters);
}
private async Task ThatFirstMethod(any parameters)
{
await Task.Run(() => {
ThatFirstTimeConsumingMethod(any param);
});
await Task.Run(() => {
ThatSecondTimeConsumingMethod(any param);
});
}
private bool ThatFirstTimeConsumingMethod(any param)
{
//code
return true;
}
.....
If you have to access UI Elements you will not be able to do so, beacause your "heavy" methods are being processed by another Thread.
Here's a solution :
private bool ThatFirstTimeConsumingMethod(any param)
{
string s = "";
Application.Current.Dispatcher.Invoke(() => {
s = myTextBox.Text;
});
// do what you want with the s variable
Application.Current.Dispatcher.Invoke(() => {
myTextBox.Text = s;
});
return true;
}
So basically, you're asking your main Thread to execute an action (where your UI is getting processed) with Application.Current.Dispatcher.Invoke. And when you're in the await Task.Run you're asking another thread to process the heavy action.

How to display the execution of each parralel Task independenly on Blazor WASM?

Description:
There is a page, which should display results from two independent asynchronous operations, for example - REST requests.
To display the first task result (doing requests through Blazor lifecycle methods) we need to await execution of both tasks, despite the data from the first task being obtained by service.
Question:
How to display the result from the first task without awaiting the execution of the second task?
Consider this code as an example.
Results of the first task will be displayed on the page only after the execution of the second task.
Service
public class TaskService : ITaskService
{
public async Task<string> FirstTask()
{
const int timeOut = 500;
await Task.Delay(timeOut);
Console.WriteLine(timeOut);
return nameof(FirstTask) + timeOut;
}
public async Task<string> SecondTask()
{
const int timeOut = 2500;
await Task.Delay(timeOut);
Console.WriteLine(timeOut);
return nameof(SecondTask) + timeOut;
}
}
Page
#page "/task-test"
#inject ITaskService _taskService
<h3>TaskTest</h3>
<p>First task: #_firstString</p>
<p>Second task: #_secondString</p>
#code {
private string _firstString = "Init value";
private string _secondString = "Init value";
protected override async Task OnInitializedAsync()
{
var firstTask = _taskService.FirstTask();
var secondTask = _taskService.SecondTask();
await Task.WhenAll(firstTask, secondTask);
_firstString = firstTask.Result;
_secondString = secondTask.Result;
}
}
When FirstTask() always finishes first, you can simplify your method as #KirkWoll commented. That looks like this:
protected override async Task OnInitializedAsync()
{
_firstString = await _taskService.FirstTask();
StateHasChanged();
_secondString = await _taskService.SecondTask();
}
I have a solution to wait for both, independently. It's not very pretty though.
protected override async Task OnInitializedAsync()
{
var firstTask = async () =>
{ _firstString = await _taskService.FirstTask();
StateHasChanged();
};
var secondTask = async () =>
{ _secondString = await _taskService.SecondTask();
StateHasChanged();
};
await Task.WhenAll(firstTask(), secondTask()); // note the ()
}
Similar to Henk's answer, but different syntax for comparison.
protected override Task OnInitializedAsync()
{
var firstTask = _taskService.FirstTask()
.ContinueWith(t=> {
_firstString=t.Result;
StateHasChanged();
});
var secondTask = _taskService.SecondTask()
.ContinueWith(t=> {
_secondString = t.Result;
StateHasChanged();
});
return Task.WhenAll(firstTask, secondTask);
}

C# Use async/await on UdpClient Receive

The following code uses Task to receive asyncronously and shows the received result in the console:
private void ReceiveMessage()
{
Task.Run(async() =>
{
using(var udpClient = new UdpClient(15000))
{
while(true)
{
var receivedResult = await udpClient.ReceiveAsync();
Console.Write(Encoding.ASCII.GetString(receivedResult.Buffer));
}
}
});
}
I want to learn how to use async/await functions so I would like to know how to make the function ReceiveMessage() asynchronously by using async/await?
If you want the whole method to be awaitable, simply change it to that:
private async Task ReceiveMessage()
{
using(var udpClient = new UdpClient(15000))
{
while(true)
{
var receivedResult = await udpClient.ReceiveAsync();
Console.Write(Encoding.ASCII.GetString(receivedResult.Buffer));
}
}
}
You don't need Task.Run() anymore, which would use a thread. That thread is not needed. The method now returns to the caller while awaiting ReceiveAsync().
When ReceiveAsync() finishes, the method is (eventually) resumed at Console.WriteLine().
The code you have is valid if you want it to be fire and forget, listening on a separate thread. If you not want this, I would remove the Task.Run and be sure to return a Task in your method, like this:
private async Task ReceiveMessage()
{
using (var udpClient = new UdpClient(15000))
{
while (true)
{
var receivedResult = await udpClient.ReceiveAsync();
Console.Write(Encoding.ASCII.GetString(receivedResult.Buffer));
}
}
}
Simply add another async/await to your function:
private async void receiveMessage()
{
await Task.Run(async() =>
{
using(var udpClient = new UdpClient(15000))
{
while(true)
{
var receivedResult = await udpClient.ReceiveAsync();
Console.Write(Encoding.ASCII.GetString(receivedResult.Buffer));
}
}
});
}
If you want to write a method being awaitable, return a task:
private Task foo(){
Task doStuff = new Task(() => {});
doStuff.Start();
return doStuff;
};
//Another method
private async void bar()
{
await foo();
}
Update:
As mentioned below, you do really not need the execution of that task on a thread inside the thread pool. It's not wrong, but useless. For better usage, you can use:
private async void receiveMessage()
{
using(var udpClient = new UdpClient(15000))
{
while(true)
{
var receivedResult = await udpClient.ReceiveAsync();
Console.Write(Encoding.ASCII.GetString(receivedResult.Buffer));
}
}
}

Running parallel async tasks?

So I've been searching StackOverflow/Google for different methods of running multiple async tasks concurrently. There seemed to be quite the debate between different methods and I just wanted to get some clarification. I'm writing a program to execute a JSON POST request until the server returns a status code of 200. Let's say I want to run 5 of theses tasks in parallel until one returns a status code of 200. Please try not to stray away from the topic, I have no control over the server! Here's my current code,
static bool status = false;
public static async Task getSessionAsync() {
while(!status) { ... }
}
public static async Task doMoreStuff() {
...
}
public static async Task RunAsync()
{
await getSessionAsync ();
await doMoreStuff();
}
public static void Main (string[] args)
{
Task.WhenAll(RunAsync()).GetAwaiter().GetResult();
}
Basically, I'm wondering if it's wrong for me to approach it like this,
public static async Task RunAsync()
{
for(int i = 0; i < 5; i++) {
await getSessionAsync ();
}
await doMoreStuff();
}
This will not run in parallel:
public static async Task RunAsync()
{
for(int i = 0; i < 5; i++) {
await getSessionAsync ();
}
await doMoreStuff();
}
You have to use Task.WhenAny()
public static async Task RunAsync()
{
var tasks = new List<Task>();
for(int i = 0; i < 5; i++) {
tasks.Add(getSessionAsync());
}
await Task.WhenAny(tasks);
await doMoreStuff();
}
If you do not need your current context (i.e. when you are writing a Library and not Frontend code), don't forget to use ConfigureAwait(false) after each await.
Assuming:
private Task<MySession> GetSessionAsync()
{
// ...
}
Option 1
Task.WhenAny
var session = await await Task.WhenAny(Enumerable.Range(0, 5).Select(_ => GetSessionAsync()));
Option 2
You could use the Rx LINQ method called Amb which will observe only the first Observable that returns something.
var session = await Enumerable.Range(0, 5).Select(_ => GetSessionAsync().ToObservable()).Amb().ToTask();

how to have children thread synchronize with main thread

i have below code, looks like the await statement in ApiClass will cause function "AControllerMethodInAspMVC" to return earlier before each api.GetResultFromAnotherService is finished.
the main thread return before all children thread finished. is there a way to fix this issue?
private ApiClass api = new ApiClass();
[HttpPost]
public Task<JsonResult> AControllerMethodInAspMVC()
{
var arrayOfItem = …;
List<object> resultObjs = new List<object>();
var resultLock = new SemaphoreSlim(1);
Parallel.ForEach(
arrayOfItem,
async item =>
{
var result = await api.GetResultFromAnotherService(item.id);
var resultObj = new {
// prepare resultObj from result
};
await resultLock.WaitAsync();
resultObjs.add(resultObj);
resultLock.Release();
});
return Task.FromResult(this.Json(resultObjs));
}
Public class ApiClass
{
Public async Task<string> GetResultFromAnotherService(string id)
{
….
…
await Call AnAsyncOperationToGetResult
…
…
}
}
Parallel.ForEach() does not understand async, so your lambda is compiled as async void. What this means is that as soon as you hit the await, the ForEach() thinks the iteration is complete and continues with another iteration.
One way to fix that would be to first start all of the service calls at the same time and then wait for all of them to complete using Task.WhenAll():
public async Task<JsonResult> AControllerMethodInAspMVC()
{
var arrayOfItem = …;
var tasks = arrayOfItem.Select(
item => api.GetResultFromAnotherService(item.id));
return await Task.WhenAll(tasks);
}
If you want to limit how many times is the service call executed in parallel, you could use SemaphoreSlim's WaitAsync():
var semaphore = new SemaphoreSlim(degreeOfParallelism);
var tasks = arrayOfItem.Select(
async item =>
{
await semaphore.WaitAsync();
try
{
return await api.GetResultFromAnotherService(item.id);
}
finally
{
sempahore.Release();
}
});

Categories