In the event handler for the "connect" event, I need to start a task that may take a long time to complete, and I need to wait for its result. While the task is running, the "connect" event may be raised again, which should interrupt the previous operation and start a new one.
The problem is that the "connect" event can be raised from different threads. So cancellation of the previous task should be thread safe. How can I achieve a thread-safe cancellation of the previous task?
I found these two answers but none of them contains a thread-safe solution:
- How to cancel an existing task and run a new task when it completes?
- How could I cancel task with different threads?
I came up with a following solution (which is not thread-safe):
CancellationTokenSource cts;
async void OnConnected()
{
cts?.Cancel();
var currentCts = cts = new CancellationTokenSource();
try
{
var result = await LongRunningTask(currentCts.Token);
//Do something with result if LongRinningTask was not cancelled
}
finally
{
currentCts.Dispose();
}
}
await Task<bool> LongRunningTask(CancellationToken ct)
{
try
{
await Task.Delay(TimeSpan.FromHours(1), ct);
return true;
}
catch(Exception)
{
return false;
}
}
Is it sufficient to achieve thread-safety just wrap the work with CancellationTokonSource
in the lock statement
? Like that:
CancellationTokenSource cts;
readonly object ctsSync = new object();
async void OnConnected()
{
lock(ctsSync)
{
cts?.Cancel();
var currentCts = cts = new CancellationTokenSource();
}
try
{
var result = await LongRunningTask(currentCts.Token);
//Do something with result if LongRinningTask was not cancelled
}
finally
{
currentCts.Dispose();
}
}
Is there better solution for these kind of problems?