0

Addition after comments in the end

I have a windows form with a fairly long running method. So I thought I'd make it async.

Because sometimes the operator does not want to wait until it is finished, I give the opportunity to cancel the task:

private CancellationTokenSource = new CancellationTokenSource();

private async Task<TResult> DoLongProcessingAsync(CancellationToken token)
{
     // do some long async processing; allow cancelling
     await Task.Delay(TimeSpan.FromSeconds(30), token);
     return new TResult() {...};
}

public async void ButtonStart_Clicked(object sender, ...)
{
    this.buttonStart.Enabled = false;
    this.ShowBusy(true);
    await this.DoLongProcessing(this.cancellationTokenSource.Token);
    this.ShowBusy(false);
    this.buttonStart.Enabled = true;
}

Now suppose the operator closes the form before DoLongProcessingAsync is completed.

private async void OnFormClosing(object sender, ...)
{
    bool closingAlloged = this.AskOperatorIfClosingAllowed();
    if (closingAllowed)
    {
        this.cancellationTokenSource.Cancel();

        // TODO: await until DoLongProcessingAsync is finished
    }
}

How do I await until LongProcessing is finished?

Addition

Some said I should await the task. But alas, the Task is a local variable inside ButtonStart_Clicked. I'm not sure if I should make it a field inside the class.

Until now I wasn't planning it, but as long as it is a local variable, I can start several tasks, by clicking several times, and cancel them all by cancelling the CancellationTokenSource.

Or is this really a no-no?

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
  • 5
    If you want to know when the task is finished, then you `await` it, just like you did in the other method that wanted to know when the operation was finished. – Servy Dec 18 '20 at 15:55
  • 2
    `await` it and wrap it into a try-catch where you anticipate an `OperationCanceledException` if the awaited method calls internally the `ThrowIfCancellationRequested` method on the passed token. – Peter Csala Dec 18 '20 at 16:00
  • I think this might be interesting: https://stackoverflow.com/questions/16656523/awaiting-asynchronous-function-inside-formclosing-event. –  Dec 18 '20 at 16:01
  • Related: [.NET Async in shutdown methods?](https://stackoverflow.com/questions/58406366/net-async-in-shutdown-methods) It would be much simpler if you could ensure that the cancellation will happen instantly, so that you can `Wait` synchronously the pending task instead of `await`ing it. – Theodor Zoulias Dec 18 '20 at 16:33
  • The cancellation will end within a second or so, So the cancellation won't be a problem. I think I'll have to make my Task a field, and ensure there is only one Task running. It feels awkward though, that the Task still exists, even if it is completed. – Harald Coppoolse Dec 21 '20 at 07:38

1 Answers1

0

You are already (a)waiting for the task inside ButtonStart_Clicked method. After cancelling a Task, inside OnFormClosing, you can catch a TaskCanceledException:

public async void ButtonStart_Clicked(object sender, ...)
{
    this.buttonStart.Enabled = false;
    this.ShowBusy(true);
    try
    {
        await this.DoLongProcessing(this.cancellationTokenSource.Token);
    }
    catch (TaskCanceledException ex)
    {
        // handle task cancelled here
    }
    catch (Exception ex)
    {
        //handle other exceptions here
    }
    finally
    {
        this.ShowBusy(false);
        this.buttonStart.Enabled = true;
    }
}
jalepi
  • 184
  • 3
  • But I am in OnFormClosing. How do I know that the ButtonStart_Clicked has completed? Of course I can wait for a SemaphoreSlim, but isn't there a smarter way? – Harald Coppoolse Dec 19 '20 at 14:19