I have a C# GUI application where when one clicks on a button and then MyMethod starts running async way and it continuously(inside a do while loop) calls MyTask. MyTask writes and reads data from a port. And these data is passed to MyProgressMethod for further proccess.
I want to implement a button which would first cancel/stop MyTask and then close the port.
Being new with this async way, I relied on some online examples which I stumbled upon and the rest were a difficult to grasp. Based on what I read, I came up with the following to achieve cancellation with a button. But I don't quite understand the mechanism and wondering whether the following way is correct:
Declaring a CancellationTokenSource object at the very beginning of class:
CancellationTokenSource my_cancelationTokenSource = null;
The button click event. Button event calls MyMethod:
private void Button_Click(object sender, RoutedEventArgs e)
{
//Some code
MyMethod();
}
MyMethod calls MyTask each second and passes data to MyProgressMethod:
private async void MyMethod()
{
my_cancelationTokenSource = new CancellationTokenSource();
do
{
await Task.Delay(1000);
//Process some code
byte[] my_received_data = await MyTask(my_sent_data, my_cancelationTokenSource.Token);
MyProgressMethod(my_received_data, my_sent_data);
}
while (true);
}
MyTask read and writes to the port(Needs to be cenceledbefore the port is closed):
private async Task<byte[]> MyTask(byte[] my_sent_data, CancellationToken cancelToken)
{
await Task.Delay(200, cancelToken);//??? What should happen here?
//Some code
}
Button event for canceling task and then closing the port:
private void Button_Disconnect_Click(object sender, RoutedEventArgs e)
{
my_cancelationTokenSource.Cancel();
if (my_port.IsOpen)
{
my_port.Close();
}
}
How can this code be optimized for stability?(i.e. port should only be closed after task is cancelled)
The solution here is to not close the port directly from the Disconnect button. Instead, cancel the token, and catch OperationCanceledException
in MyMethod
:
private CancellationTokenSource my_cancelationTokenSource;
private async void MyMethod()
{
my_cancelationTokenSource = new CancellationTokenSource();
try
{
while (true)
{
await Task.Delay(1000, my_cancelationTokenSource.Token);
//Process some code
byte[] my_received_data = await MyTask(my_sent_data, my_cancelationTokenSource.Token);
MyProgressMethod(my_received_data, my_sent_data);
}
}
catch (OperationCanceledException)
{
try
{
my_cancelationTokenSource.Dispose();
my_cancelationTokenSource = null;
my_port.Dispose();
}
catch { }
}
}
private void Button_Disconnect_Click(object sender, RoutedEventArgs e)
{
my_cancelationTokenSource?.Cancel();
}
Notes:
my_cancelationTokenSource
becomes a field rather than a local variable.Task.Delay
functions also. (It's unclear why you need the delays, normally you just wait for a response on the port).try/catch
the closure of the port, which you should do via Dispose
, just in case it throws.