I wrote a visual studio 2015 c# program for reading from a custom Ethernet device some data. I need to use async await instructions because the data will be read all together on scheduled times.
I use a custom .NET library for read data; this is my code:
private void timerPollingData_Tick(object sender, EventArgs e)
{
readDevice_01();
readDevice_02();
// and so on for all devices ...
}
private async void readDevice_01()
{
var result = await getDataDevice_01();
// Save data
}
private async void readDevice_02()
{
var result = await getDataDevice_02();
// Save data
}
private Task<string> getDataDevice_01()
{
MyCustomLibrary readDevice = new MyCustomLibrary.Master();
return Task.Factory.StartNew(() => readDevice.ReadHoldingRegister(... some parameters ...).ToString());
}
private Task<string> getDataDevice_02()
{
MyCustomLibrary readDevice = new MyCustomLibrary.Master();
return Task.Factory.StartNew(() => readDevice.ReadHoldingRegister(... some parameters ...).ToString());
}
My doubt:
what is the best practice for handle exception of each Task? I need to understand what devices are unplug from Ethernet or switch off and then STOP the TASK used to retrieve data from it.
Thanks a lot in advance for your help.
You should avoid async void; use async Task for everything except async event handlers. For more information, see my article on async best practices.
Also, you should not use StartNew; it's a low-level, very dangerous API with inappropriate default parameter values. Use Task.Run instead of StartNew. For more information, see my blog post on StartNew is dangerous.
I need to use async await instructions because the data will be read all together on scheduled times.
Asynchrony is one form of concurrency, which you can use with Task.Run if your device API does not have asynchronous methods:
private async void timerPollingData_Tick(object sender, EventArgs e)
{
var task1 = readDevice_01();
var task2 = readDevice_02();
// and so on for all devices ...
await Task.WhenAll(task1, task2, ...);
}
private async Task readDevice_01()
{
var result = await Task.Run(() => getDataDevice_01());
// Save data
}
private string getDataDevice_01()
{
MyCustomLibrary readDevice = new MyCustomLibrary.Master();
return readDevice.ReadHoldingRegister(... some parameters ...).ToString();
}
If your API had a ReadHoldingRegisterAsync method, then this would be more naturally expressed as:
private async void timerPollingData_Tick(object sender, EventArgs e)
{
var task1 = readDevice_01Async();
var task2 = readDevice_02Async();
// and so on for all devices ...
await Task.WhenAll(task1, task2, ...);
}
private async Task readDevice_01Async()
{
var result = await getDataDevice_01Async();
// Save data
}
private async Task<string> getDataDevice_01Async()
{
MyCustomLibrary readDevice = new MyCustomLibrary.Master();
var result = await readDevice.ReadHoldingRegisterAsync(... some parameters ...);
return result.ToString();
}
My doubt: what is the best practice for handle exception of each Task? I need to understand what devices are unplug from Ethernet or switch off and then STOP the TASK used to retrieve data from it.
The best practices for getting exceptions from tasks are to await them.
You don't have to worry about stopping a task after it raised an exception. By the time the task reports its exception, it has already stopped.
Related
I am trying to use the following technique to be able to have a worker task executing some operations, with a 10 sec timeout and without blocking the application.
internal void ReadAll()
{
var data = new byte[1];
Task.Factory.StartNew(() =>
{
var ct = new CancellationTokenSource();
var ReadAllTask = Task.Factory.StartNew(() =>
{
// Read all information
// [omit communication exchange via COM port]
ct.Cancel();
}, ct.Token);
// First thread waiting 10s for the worker to finish
ReadAllTask.Wait(10000, ct.Token);
if (ReadAllTask.Status == TaskStatus.RanToCompletion)
{
ReadAllComplete?.Invoke(true);
}
else
{
ct.Cancel();
ReadAllComplete?.Invoke(false);
}
});
}
This method is called by pressing a button. It seems to me that in debug configuration works properly, but not in release configuration where the "first thread" never reach the wait and no event is thrown.
Your code could be a lot simpler than current version. Easiest way to make a non-blocking method for event is to mark it with async keyword and use the await keyword to start the asynchronous read operation from SerialPort.BaseStream property.
Also, CancellationTokenSource could be created with time, after that it get cancelled automatically, and the right way to cancel is to call CancellationToken.ThrowIfCancellationRequested method. async/await mechanism will invoke the event in UI context, so code could be something like this:
// async void is a recommended way to use asynchronous event handlers
private async void btnReadAll_Click(object sebder, EventArgs e)
{
var data = new byte[2];
// cancel source after 10 seconds
var cts = new CancellationTokenSource(10000);
// Read all information
// [omit communication exchange via COM port]
// async operation with BaseStream
var result = await SerialPort.BaseStream.ReadAsync(data, 0, 2, cts.Token);
/*
* if you can't use the BaseStream methods, simply call this method here
* cts.Token.ThrowIfCancellationRequested();
*/
// this code would run only if everything is ok
// check result here in your own way
var boolFlag = result != null;
ReadAllComplete?.Invoke(boolFlag);
}
Here's just a quick rewrite to remove the event and wrap what appears to be a synchronous IO API in an async one. If at all possible you should switch to a true async API and drop the Task.Run.
private CancellationTokenSource cts;
public async void MyButtonhandler(object sender, EventArgs e) {
cts = new CancellationTokenSource();
try {
var result = await Task.Run(() => ReadAll(cts));
if (result) {
//success
} else {
//failure
}
} catch (TaskCanceledException ex) {
}
}
internal async Task<bool> ReadAll(CancellationTokenSource cts) {
byte[] data = new byte[1];
var timeout = TimeSpan.FromSeconds(10);
var ReadAllTask = Task.Run(() => {
// Read all information
// [omit communication exchange via COM port]
}, cts.Token);
if (await Task.WhenAny(ReadAllTask, Task.Delay(timeout)) == ReadAllTask) {
return true;
}
cts.Cancel();
return false;
}
Reading comments and answers to my question I learned a couple of useful things that solve my problem:
CancellationTokenSource can have an implicit timeout
use Task.Run instead Task.Factory.StartNew
don't need to cancel the task, the cts will do the work
Now my code is simpler and it works:
private void Read_All_Button_Click(object sender, RoutedEventArgs e)
{
// Start timedout task that will send all necessary commands
CancellationTokenSource cts = new CancellationTokenSource(10000);
Task.Run(() =>
{
oCommandSets.ReadAll(cts);
}, cts.Token);
}
and
internal void ReadAll(CancellationTokenSource cts)
{
// [communication]
if (cts.IsCancellationRequested)
{
ReadAllComplete?.Invoke(false);
}
else
{
ReadAllComplete?.Invoke(true);
}
}
In any case I need to learn more about multithreading.
I'm trying to learn and implement async / await keywords on my application. I'm using an API to get data then showing them on my forms. When I try to call methods from an console application there is no problem. But if I call my async methods from Form_Shown event also there no exception but methods not working.
So I'm calling my RefreshOrLoadDataToCache() method on Form_Shown event.
private async void LogTimeReport_Shown(object sender, EventArgs e)
{
// Some syncronous operations
RefreshOrLoadDataToCache(); // Async methods in it
// Some syncronous operations
}
In my this method created a task and wait for it.
private async void RefreshOrLoadDataToCache()
{
if (IsNeededToCallAPI())
{
var taskForTimeEntries = LoadTimeEntriesTemp();
Task.WhenAll(taskForTimeEntries);
DataTable dtTimeEntriesTemp = taskForTimeEntries.Result;
DataTable dtEventsTemp = LoadEventsTemp();
dtTimeEntriesTemp.Merge(dtEventsTemp);
}
else
BindGridViews();
}
This my async method.
private async Task<DataTable> LoadTimeEntriesTemp()
{
TimeEntryHandler timeHandler = new TimeEntryHandler();
TimeEntryResponse response = await timeHandler.GetTimeEntries();
DataTable dt = DatatableHelper.ToDataTable<TimeEntry>(response.TimeEntries);
foreach (DataRow drow in dt.Rows)
{
// Some operations on DataTable
}
return dt;
}
In this method I'm connecting to API and getting results. I think my problem is about this method. Because when I call this method from console application it returns data. But from form application it waits for a long time but there is no result or exception.
private async Task<TimeEntryResponse> GetTimeEntries()
{
using (var client = new AuthorizedHttpClient(_client))
{
var data = await client.GetAsync<TimeEntryResponse>(parameters);
if (data.StatusCode == HttpStatusCode.OK)
{
var response = (TimeEntryResponse)data.ContentObj;
response.Pages = int.Parse(data.Headers.GetValues("X-Pages").First());
response.Page = int.Parse(data.Headers.GetValues("X-Page").First());
response.TotalRecords = int.Parse(data.Headers.GetValues("X-Records").First());
return response;
}
return new TimeEntryResponse() { TimeEntries = null, STATUS = "ERROR" };
}
}
I thought that there is something I'm missing about asyncronous calls on windows forms. How can I fix my code ?
You have a couple of problems with your code
You mark a method as async, but you don't await on the operation inside. You currently do this because RefreshOrLoad is async void. It actually needs to be async Task, where the underlying returned task is the ongoing async operation. Then, the returned Task should be awaited on:
private async void LogTimeReport_Shown(object sender, EventArgs e)
{
// Some syncronous operations
await RefreshOrLoadDataToCache(); // Async methods in it
// Some syncronous operations
}
RefreshOrLoad is an async method. You use Task.WhenAll, which is used for asynchronously waiting on multiple tasks, but you don't await on it either. Then, you call .Result, which causes your code to effectively deadlock. All that's needed is to await the task returning from LoadTimeEntriesTemp:
private async Task RefreshOrLoadDataToCache()
{
if (IsNeededToCallAPI())
{
DataTable dtTimeEntriesTemp = await LoadTimeEntriesTemp();
DataTable dtEventsTemp = LoadEventsTemp();
dtTimeEntriesTemp.Merge(dtEventsTemp);
}
else
BindGridViews();
}
I'd also note that you should use the *Async postfix with your async methods.
When fixing these, you'll see that your code behaves as expected, being asynchronous all the way down.
You problem here:
var taskForTimeEntries = LoadTimeEntriesTemp();
Task.WhenAll(taskForTimeEntries);
DataTable dtTimeEntriesTemp = taskForTimeEntries.Result;
At first, why do you use Task.WhenAll when you have just one task? That way you leak the task returned by Task.WhenAll which will be completed and indicate that all your tasks that are passed to Task.WhenAll are completed. It will wait for the task synchronously which will cause deadlock.
There is a rule for async/await which states await in all ways
So right approach is:
DataTable dtTimeEntriesTemp = await LoadTimeEntriesTemp();
Also, you should await on RefreshOrLoadDataToCache(); in your event handler if you want to do synchronous operations related to its result.
Here is a great article by Stephen Cleary Don't Block on Async Code which describes your problem in more details.
Method RefreshOrLoadDataToCache() is marked as async yet it does not use await on Task.WhenAll() and LogTimeReport_Shown() does not have to be async. :
private async void RefreshOrLoadDataToCache()
{
if (IsNeededToCallAPI())
{
var taskForTimeEntries = LoadTimeEntriesTemp();
DataTable dtTimeEntriesTemp = await taskForTimeEntries; // call await here
DataTable dtEventsTemp = LoadEventsTemp();
dtTimeEntriesTemp.Merge(dtEventsTemp);
}
else
BindGridViews();
}
I have multiple heavy job calculation requests. The job may take different time. By using async and await I want to take the last requested result with canceling eventually unfinished previous tasks.
Currently I'm using BackGroundWorker with setting a job ID. I used only the the result with the last requested ID.
Can I rewrite the code with using async await?
private int backtestId;
private void PrepareStrategyCalculation()
{
backtestId = backtestManager.GetNextBacktestId();
strategy.BacktestId = backtestId;
backtestManager.StartBacktestWorker(strategy.Clone());
}
private void BacktestManager_StrategyBacktested(object sender, StrategyBacktestEventArgs e)
{
if (e.BacktestObject.Strategy.BacktestId != backtestId) return;
var calculatedStrategy = e.BacktestObject.Strategy;
...
}
EDIT:
Is this a solution?
private int backtestId;
private async void PrepareStrategyCalculation()
{
backtestId = backtestManager.GetNextBacktestId();
strategy.BacktestId = backtestId;
var calculatedStrategy = await backtestManager.StartBacktestAsync(strategy.Clone());
if (calculatedStrategy.BacktestId != backtestId) return;
...
}
Assuming your code is CPU-bound, then Task.Run is a suitable substitute for BackgroundWorker.
You can use CancellationTokenSource to cancel tasks. So, something like this would work, assuming that StartBacktestAsync is called from a single-threaded context such as a UI thread:
private CancellationTokenSource _cts;
async Task StartBacktestAsync()
{
if (_cts != null)
_cts.Cancel();
_cts = new CancellationTokenSource();
try
{
var token = _cts.Token;
await Task.Run(() => Backtest(token));
}
catch (OperationCanceledException)
{
// Any special logic for a canceled operation.
}
}
void Backtest(CancellationToken token)
{
... // periodically call token.ThrowIfCancellationRequested();
}
I have a method HandleAcceptedConnection that is under Task.Run() that i want to run asynchronously(in another separate thread). I tried declaring HandleAcceptedConnection as async method and dont call await but it doesnt seem to run asynchronously. I can confirm that I can have Task.Run()(by watching the thread id) under another Task.Run() but is that recommended?
private async void Start_Click(object sender, RoutedEventArgs e)
{
var task = Task.Run(() =>
{
while (isContinue)
{
var handler = listener.Accept();
// handle connection
Log("Before");
Log("ThreadId Accept " + Thread.CurrentThread.ManagedThreadId);
// i want to run method below asynchronously. i want to
// wrap it under Task.Run() but i am already under
// Task.Run(). i set HandleAcceptedConnection as async. i thought by not
// calling await on HandleAcceptedConnection, HandleAcceptedConnection
// is asynchronous
HandleAcceptedConnection(handler);
Log("After");
isContinue = true;
}
});
await task;
}
private async Task HandleAcceptedConnection(Socket handler)
{
Log("ThreadId HandleAcceptedConnection " + Thread.CurrentThread.ManagedThreadId);
Log("Under HandleAcceptedConnection");
Thread.Sleep(10000);
}
When i run this, logs says
Before
Under HandleAcceptedConnection
After
i want
Before
After
Under HandleAcceptedConnection
i want HandleAcceptedConnection to be run asynchronously. Should i wrap it under another Task.Run or it is already asynchronous?
Did you try
private async Task HandleAcceptedConnection(Socket handler)
{
Thread.Sleep(1000);
Log("Under HandleAcceptedConnection");
}
Because doing something on another thread doesn't mean it'll be delayed.
You should be using AcceptTcpClientAsync, then you won't need extra threads. Check this answer for an example. Don't use a synchronous API when there is a naturally asynchronous version of it available.
Updated to address the comment. Nothing prevents you from using Task.Run from inside Task.Run, you code might look like this (untested):
private async void Start_Click(object sender, RoutedEventArgs e)
{
var connectionTasks = new List<Task>();
Func<Task> handleConnection = async () =>
{
var connectionTask = Task.Run(() => HandleAcceptedConnection(handler));
connectionTasks.Add(connectionTask);
await connectionTask;
connectionTasks.Remove(connectionTask);
};
var task = Task.Run(() =>
{
while (isContinue)
{
var handler = listener.Accept();
// handle connection
Log("Before");
Log("ThreadId Accept " + Thread.CurrentThread.ManagedThreadId);
var connectionTask = handleConnection();
Log("After");
isContinue = true;
}
});
await task;
}
Let's suppose I have this simple snippet:
async void button_Click(object sender, RoutedEventArgs e)
{
await Task.Factory.StartNew(() =>
{
Console.WriteLine("start");
Thread.Sleep(5000);
Console.WriteLine("end");
});
}
Obviously, everytime I push that button a new task is started even when a previous task still runs. How would I postpone any new task until all previous tasks have finished?
Some more details:
In the example above, each new task is identical to the task before. However, in the original context the sequence of tasks matters: Parameters may change (I could "simulate" it by using DateTime.Now.Ticks).
The tasks should be executed in the order they are "registered". Specificly, my program will talk to a serial device. I've done this before with a background thread utilizing a BlockingCollection. However, this time there's a strict request/response-protocol and I'd like to use async/await if it is possible.
Possible solution:
I could imagine creating tasks and storing them in a list. But how would I execute the tasks with respect to the requirements? Or should I return to the thread-based solution I have used before?
I recommend using a SemaphoreSlim for synchronization. However, you want to avoid Task.Factory.StartNew (as I explain on my blog), and also definitely avoid async void (as I explain in the MSDN article).
private SemaphoreSlim _mutex = new SemaphoreSlim(1);
async void button_Click(object sender, RoutedEventArgs e)
{
await Task.Run(async () =>
{
await _mutex.WaitAsync();
try
{
Console.WriteLine("start");
Thread.Sleep(5000);
Console.WriteLine("end");
}
finally
{
_mutex.Release();
}
});
}
You could wait on a SemaphoreSlim asynchronously and release it once the job is done. Don't forget to configure the semaphore initialcount to 1.
private static SemaphoreSlim semaphore = new SemaphoreSlim(1);
private async static void DoSomethingAsync()
{
await semaphore.WaitAsync();
try
{
await Task.Factory.StartNew(() =>
{
Console.WriteLine("start");
Thread.Sleep(5000);
Console.WriteLine("end");
});
}
finally
{
semaphore.Release();
}
}
private static void Main(string[] args)
{
DoSomethingAsync();
DoSomethingAsync();
Console.Read();
}
What about trying the Dataflow.ActionBlock<T> with the (default) max degree of parallelism of 1. This way you don't need to worry about any of the thread safety / locking concerns.
It could look something like:
...
var _block = new ActionBlock<bool>(async b =>
{
Console.WriteLine("start");
await Task.Delay(5000);
Console.WriteLine("end");
});
...
async void button_Click(object sender, RoutedEventArgs e)
{
await _block.SendAsync(true);
}
You could also setup the ActionBlock to receive a Task or Func<Task>, and simply run / await this input. Which would allow multiple operations to be queued and awaited from different sources.
I might be missing something, but I don't think SemaphoreSlim is needed for the OP's scenario. I'd do it the following way. Basically, the code just await the previous pending instance of the task before continuing (no exception handling for clarity):
// the current pending task (initially a completed stub)
Task _pendingTask = Task.FromResult<bool>(true);
async void button_Click(object sender, RoutedEventArgs e)
{
var previousTask = _pendingTask;
_pendingTask = Task.Run(async () =>
{
await previousTask;
Console.WriteLine("start");
Thread.Sleep(5000);
Console.WriteLine("end");
});
// the following "await" is optional,
// you only need it if you have other things to do
// inside "button_Click" when "_pendingTask" is completed
await _pendingTask;
}
[UPDATE] To address the comment, here's a thread-safe version, when button_Click can be called concurrently:
Task _pendingTask = Task.FromResult<bool>(true);
object _pendingTaskLock = new Object();
async void button_Click(object sender, RoutedEventArgs e)
{
Task thisTask;
lock (_pendingTaskLock)
{
var previousTask = _pendingTask;
// note the "Task.Run" lambda doesn't stay in the lock
thisTask = Task.Run(async () =>
{
await previousTask;
Console.WriteLine("start");
Thread.Sleep(5000);
Console.WriteLine("end");
});
_pendingTask = thisTask;
}
await thisTask;
}