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));
}
}
}
Related
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.
I have a Blazor WASM application that needs to call an API every second without blocking the UI. This codes demonstrates how I tried to do that:
List<int> testList = new();
testList.Add(1);
testList.Add(2);
testList.Add(3);
testList.Add(4);
List<int> emptyTestlist = new();
CancellationTokenSource cts;
Test();
void Test()
{
Parallel.Invoke(async () =>
{
do
{
Console.WriteLine("Start");
await Task.Delay(1000);
await Test2();
Console.WriteLine("END");
} while (true);
});
}
Console.ReadLine();
async ValueTask Test2()
{
emptyTestlist.Clear();
cts = new();
await Parallel.ForEachAsync(testList, cts.Token, async (test, token) =>
{
await Test4(test);
});
foreach (var test in emptyTestlist)
{
await Test3(test);
}
}
async Task Test4(int i)
{
await Task.Delay(300);
//Console.WriteLine("if I Add this console.WriteLine It's added perfectly");
emptyTestlist.Add(i);
Console.WriteLine($"from TEST4: {i}");
}
async Task Test3(int i)
{
Console.WriteLine($"TEST3 {i}.");
await Task.Delay(1000);
Console.WriteLine($"TEST3 {i}, after 1sec");
}
If I comment the line Console.WriteLine("if I Add this console.WriteLine It's added perfectly");, it's not adding perfectly. (emptyTestlist.Count is not always 4). But if I add Console.WriteLine before emptyTestlist.Add(i) it works correctly (emptyTestlist.Count is always 4).
I don't know how to solve it. What's the problem?
The easiest way to poll an API is to use a timer:
#code {
private List<Customer> custs=new List<Customer>();
private System.Threading.Timer timer;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
custs = await Http.GetFromJsonAsync<List<Customer>>(url);
timer = new System.Threading.Timer(async _ =>
{
custs = await Http.GetFromJsonAsync<List<Customer>>("/api/customers");
InvokeAsync(StateHasChanged);
}, null, 1000, 1000);
}
In this case InvokeAsync(StateHasChanged); is needed because the state was modified from a timer thread and Blazor has no idea the data changed.
If we wanted to add the results to a list though, we'd either have to use a lock or a thread-safe collection, like a ConcurrentQueue.
#code {
private ConcurrentQueue<Customer> custs=new ConcurrentQueue<Customer>();
private System.Threading.Timer timer;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
custs = await Http.GetFromJsonAsync<List<Customer>>(url);
timer = new System.Threading.Timer(async _ =>
{
var results = await Http.GetFromJsonAsync<List<Customer>>("/api/customers");
foreach(var c in results)
{
custs.Enqueue(c);
}
InvokeAsync(StateHasChanged);
}, null, 1000, 1000);
}
Polling an API every second just in case there's any new data isn't very efficient though. It would be better to have the API notify clients of any new data using eg SignalR or Push Notifications
Borrowing from the documentation example this would be enough to receive messages from the server:
#code {
private HubConnection hubConnection;
private List<string> messages = new List<string>();
private string userInput;
private string messageInput;
protected override async Task OnInitializedAsync()
{
hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/chathub"))
.Build();
hubConnection.On<string, string>("ReceiveMessage", (user, message) =>
{
var encodedMsg = $"{user}: {message}";
messages.Add(encodedMsg);
StateHasChanged();
});
await hubConnection.StartAsync();
}
I simple do not know how to make the following code work, it is suppose to keep running forever inside an infinite loop, and in fact it will work when I remove await Task.Delay from the method Method_FOO, but I need Method_FOO to be async.
I think to make this work the Thread.Start() method needs to be "waitable" (not just the code it runs), but Thread.Start is void. I notice that if I block the execution with eg.: Console.ReadLine it will print the Worked string, but this is not a solution, and is terrible in real life.
This code is just an example, but the threads need to run inside an infinite loop (it is not something I can change), and I need async methods because I need to consume some websockets, and looks like there is no sync client websocket class in C#.
But still, there must be a simple/decent solution for this problem.
public static class Program
{
public static async Task Main(string[] args)
{
var cancellationTokenSource = new CancellationTokenSource();
var thread1 = new Thread(async () => await Run(cancellationTokenSource.Token, "threadName1", Method_FOO));
var thread2 = new Thread(async () => await Run(cancellationTokenSource.Token, "threadName2", Method_FOO));
thread1.Start();
thread2.Start();
}
private static async Task Method_FOO(CancellationToken cancellationToken)
{
Console.WriteLine("It is called...");
await Task.Delay(300, cancellationToken);
//never reach this part
Console.WriteLine("Worked ...");
}
// workd but it is not async
//private static Task Method_FOO(CancellationToken cancellationToken)
//{
// Console.WriteLine("It is called...");
// Console.WriteLine("Worked ...");
// return Task.CompletedTask;
//}
private static async Task Run(CancellationToken cancellationToken, string threadName, Func<CancellationToken, Task> function)
{
try
{
while (true)
{
await function(cancellationToken);
Console.WriteLine($"{threadName} waiting ...");
cancellationToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
At this point in history, new Thread is pretty much only useful for COM interop. In every other scenario, there are better solutions. In this case, you can send work to the thread pool by using Task.Run:
public static async Task Main(string[] args)
{
var cancellationTokenSource = new CancellationTokenSource();
var task1 = Task.Run(async () => await Run(cancellationTokenSource.Token, "threadName1", Method_FOO));
var task2 = Task.Run(async () => await Run(cancellationTokenSource.Token, "threadName2", Method_FOO));
await Task.WhenAll(task1, task2);
}
Thread don't wait task's end. When the 3 threads (main, thread1, thread2), the program is closed and running task killed.
You can make the method Run synchronous and manualy wait the task end, like :
public static class Program
{
public static async Task Main(string[] args)
{
var cancellationTokenSource = new CancellationTokenSource();
var thread1 = new Thread(() => Run(cancellationTokenSource.Token, "threadName1", Method_FOO));
var thread2 = new Thread(() => Run(cancellationTokenSource.Token, "threadName2", Method_FOO));
thread1.Start();
thread2.Start();
}
private static async Task Method_FOO(CancellationToken cancellationToken)
{
Console.WriteLine("It is called...");
await Task.Delay(300, cancellationToken);
Console.WriteLine("Worked ...");
}
private static void Run(CancellationToken cancellationToken, string threadName, Func<CancellationToken, Task> function)
{
try
{
while (true)
{
function(cancellationToken).Wait();
Console.WriteLine($"{threadName} waiting ...");
cancellationToken.WaitHandle.WaitOne(TimeSpan.FromSeconds(1));
}
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
Edit : Why not directly use the tasks?
public static void Main(string[] args)
{
var cancellationTokenSource = new CancellationTokenSource();
var task1 = Run(cancellationTokenSource.Token, "threadName1", Method_FOO);
var task2 = Run(cancellationTokenSource.Token, "threadName2", Method_FOO);
Task.Delay(TimeSpan.FromSeconds(5)).ContinueWith(t => cancellationTokenSource.Cancel());
Task.WaitAll(task1, task2);
}
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());
}
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();
}
});