CancellationToken and CancellationTokenSource-How to use it?

后端 未结 3 2004
北荒
北荒 2021-01-02 12:23

I have a UI button called Load. It spawns a thread, which in turn spawns a task. There is a wait on the task, and if it expires the task gets cancelled. The Load button is n

3条回答
  •  情话喂你
    2021-01-02 12:52

    There are a few mistakes in your code that make things confusing.

    First, you're using Thread.Sleep instead of Task.Delay or some other timer-based method (I strongly recommend writing your own if you don't have access to Task.Delay). Sleep is a blocking wait and can't be conditioned on the cancel token. The result is precious thread pool threads being held hostage for multiple seconds, even if the operation is cancelled. This could result in the effects of later button presses being held up by earlier ones.

    Second, at the end of the wait you're cancelling _source but this refers to the current _value of source not the value at the time the button was pressed. Earlier button presses will cancel later button press effects instead of their own.

    Third, you're disposing the cancel token source on one thread while racing to cancel it on another. You're lucky you're not getting object disposed exceptions.

    Fourth, it would be ideal to use async in this sort of situation. You mentioned you were only on .Net 4.0, though.

    Fixing the first three things should make what's going on easier to reason about:

    CancellationTokenSource _prevSource = new CancellationTokenSource();
    public void OnButtonPress() {
        var curSource = new CancellationTokenSource();
        _prevSource.Cancel();
        _prevSource = curSource;
    
        MyCustomDelay(TimeSpan.FromSeconds(5), curSource.Token).ContinueWith(t => {
            curSource.Cancel();
        }, TaskContinuationOptions.OnlyOnRanToCompletion);
    
        var r = MyCustomDelay(TimeSpan.FromSeconds(20), curSource.Token).ContinueWith(t => {
            curSource.ThrowIfCancellationRequested();
        }, TaskContinuationOptions.OnlyOnRanToCompletion);
        // after 5 seconds the token r's delay is conditions on is cancelled
        // so r is cancelled, due to the continuation specifying OnlyOnRanToCompletion
        // the ThrowIfCancellationRequested line won't be executed
        // although if we removed the cancel-after-5-seconds bit then it would be
    }
    

提交回复
热议问题