Usage of CancellationToken and CancellationTokenSource in C #

I didn't pay attention to this before, so I did it net core, I found that CancellationToken is more and more common.

No wonder, it turns out net framework does not use many asynchronies, but The asynchronous token can be seen everywhere, and the asynchronous token can be seen everywhere! So take some time to take notes

  

  CancellationToken

CancellationToken has a constructor, which can pass in a bool type to indicate whether the current CancellationToken is in cancellation status. In addition, because CancellationToken is a structure, it also has a constructor with an empty parameter.   

    public CancellationToken();//Because it is a structure, there is an empty constructor, but it doesn't work
    public CancellationToken(bool canceled);

The properties are as follows:

    //Static property to obtain an empty CancellationToken. The callback method registered by this CancellationToken will not be triggered. Its function is similar to that obtained by using the empty constructor
    public static CancellationToken None { get; }
    //Indicates whether the current CancellationToken can be cancelled
    public bool CanBeCanceled { get; }
    //Indicates the currentCancellationTokenIs it already in cancelled status
    public bool IsCancellationRequested { get; }
    //andCancellationTokenAssociated WaitHandle Object,CancellationTokenThe registered callback method executes through this WaitHandle Realized
    public WaitHandle WaitHandle { get; }

Common methods:

    //Register callback in CancellationToken
    public CancellationTokenRegistration Register(Action callback);
    public CancellationTokenRegistration Register(Action callback, bool useSynchronizationContext);
    public CancellationTokenRegistration Register([NullableAttribute(new[] { 1, 2 })] Action<object?> callback, object? state);
    public CancellationTokenRegistration Register([NullableAttribute(new[] { 1, 2 })] Action<object?> callback, object? state, bool useSynchronizationContext);
    //When the CancellationToken is in the cancel state, system. Is thrown Operationcanceledexception exception
    public void ThrowIfCancellationRequested();

The common methods to Register callbacks are the above four Register methods. Callback is the delegate executed by the callback, useSynchronizationContext indicates whether to use the synchronization context, and state is the parameter value passed to the callback delegate

In addition, the Register method will return a CancellationTokenRegistration structure. After registering the callback, you can call the Unregister method of CancellationTokenRegistration to cancel the registration. The Unregister method will return a bool value. When the cancellation is successful, it will return true, and when the cancellation fails (for example, the callback has been executed), it will return false:

    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    var cancellationTokenRegistration = cancellationTokenSource.Token.Register(() =>
    {
        Console.WriteLine("Canceled");//No output will be performed here
    });

    //cancellationTokenSource.Cancel();
    //var result = cancellationTokenRegistration.Unregister();//result = false

    var result = cancellationTokenRegistration.Unregister();//result = true
   cancellationTokenSource.Cancel();

As mentioned above, the CancellationToken can be constructed directly using the constructor. At the same time, a parameter can be passed in to represent the current state. It should be noted that the state of the CancellationToken can be changed at most once, that is, it has never been cancelled but has been cancelled.

If true is passed in during construction, that is, the CancellationToken is in the cancelled state, the registered callback will be executed immediately at this time:

    CancellationToken cancellationToken = new CancellationToken(true);
    cancellationToken.Register(() =>
    {
        Console.WriteLine("Canceled");//The output is executed hereCanceled
    });

However, if false is passed in during construction, it indicates that the CancellationToken is not cancelled. At this time, the registered return will be in a pending state:

    CancellationToken cancellationToken = new CancellationToken(false);
    cancellationToken.Register(() =>
    {
        Console.WriteLine("Canceled");//No output will be performed here
    });

The service registered through the Register method will be executed only once!

But generally, if the CancellationToken constructed by false is passed in, it can be considered that it will not be triggered, because it has no trigger method! Therefore, generally, we will not directly use the constructor to create CancellationToken, and satellite TV uses CancellationTokenSource

 

  CancellationTokenSource

CancellationTokenSource can be understood as the controller of CancellationToken, which controls when it becomes an object in cancellation status. It has an attribute Token of CancellationToken type. As long as CancellationTokenSource is created, this Token will also be created, and the Token will be bound to this CancellationTokenSource:

    //express Token Is it in cancelled status
    public bool IsCancellationRequested { get; }
    //CancellationToken object
    public CancellationToken Token { get; }

You can directly create a CancellationTokenSource object and specify a time period. After this time period, the CancellationTokenSource will be automatically cancelled.

There are four methods to cancel the CancellationTokenSource:

    //Cancel now
    public void Cancel();
    //Cancel now
    public void Cancel(bool throwOnFirstException);
    //Cancel after a specified delay
    public void CancelAfter(int millisecondsDelay);
    //Cancel after a specified delay
    public void CancelAfter(TimeSpan delay);

There is nothing special about Cancel and the two CancelAfter methods. They mainly have a delay effect. Note the difference between the two overloads of Cancel.

First of all, as mentioned above, the CancellationToken status can only be changed once (from never cancelled to cancelled). When the CancellationToken is in the cancelled status, each callback registered in it will be executed immediately! When it is not cancelled, all registered callbacks will wait for execution.

It should be noted that when multiple callbacks are registered without cancellation, they are executed in a stack like structure order, which is registered first and then executed.

The Register of CancellationToken can Register multiple callbacks, and they may throw exceptions. The throwOnFirstException parameter indicates the processing behavior when an error is reported for the first time

throwOnFirstException = true means that the current exception will be thrown immediately, and subsequent callbacks will cancel execution

    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    try
    {
        cancellationTokenSource.Token.Register(() =>
        {
            throw new Exception("1");
        });
        cancellationTokenSource.Token.Register(() =>
        {
            throw new Exception("2");
        });

        cancellationTokenSource.Cancel(true);
    }
    catch (Exception ex)
    {
        //ex is System.Exception
    }

throwOnFirstException = false means to skip the exception of the current callback and continue to execute the effective callback. After all callbacks are executed, package all exceptions into a system Aggregateexception exception thrown!   

    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    try
    {
        cancellationTokenSource.Token.Register(() =>
        {
            throw new Exception("1");
        });
        cancellationTokenSource.Token.Register(() =>
        {
            throw new Exception("2");
        });

        cancellationTokenSource.Cancel(false);//amount to cancellationTokenSource.Cancel()
    }
    catch (Exception ex)
    {
        //ex is System.AggregateException:[Exception("2"),Exception("1")]
    }

CancellationTokenSource can also be associated with other cancellationtokens to generate a new CancellationToken. When other cancellationtokens are cancelled, the current CancellationTokenSource will be automatically triggered to perform the cancellation action!   

    CancellationTokenSource cancellationTokenSource1 = new CancellationTokenSource();
    cancellationTokenSource1.Token.Register(() =>
    {
        Console.WriteLine("Cancel1");
    });
    CancellationTokenSource cancellationTokenSource2 = new CancellationTokenSource();
    cancellationTokenSource2.Token.Register(() =>
    {
        Console.WriteLine("Cancel2");
    });
    CancellationTokenSource cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationTokenSource1.Token, cancellationTokenSource2.Token);
    cancellationTokenSource.Token.Register(() =>
    {
        Console.WriteLine("Cancel");
    });

    //cancellationTokenSource1.Cancel(); //Execute this sequential output Cancel    Cancel1
    cancellationTokenSource2.Cancel(); //Execute this sequential output Cancel    Cancel2

 

Usage scenario 1

When we create an asynchronous operation, we can pass in a CancellationToken. When the asynchronous operation is waiting for execution, we can cancel the execution of the asynchronous operation by setting the CancellationToken to cancel:

    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    var task = new Task(() =>
    {
        Thread.Sleep(1500);//The code in 2 seconds was executed Console.WriteLine(
"Execute Some Code"); }, cancellationTokenSource.Token); task.Start();//Start and wait for scheduling execution //If you find something wrong, you can cancel task implement cancellationTokenSource.Cancel(); Thread.Sleep(1000);//Wait 1 second Console.WriteLine("Task Status:" + task.Status);//Canceled

However, often, our cancellation action may not be so timely. If the asynchronous has been executed and the cancellation is invalid, we need to detect it in the asynchronous delegate ourselves:

    CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
    var task = new Task(() =>
    {
        Thread.Sleep(1500);//The code in 2 seconds was executed
        cancellationTokenSource.Token.ThrowIfCancellationRequested();
        Console.WriteLine("Execute Some Code");
    }, cancellationTokenSource.Token);

    task.Start();//Start and wait for scheduling execution

    Thread.Sleep(1000);////If you find something wrong after a period of time, you can cancel it task implement
    cancellationTokenSource.Cancel();
    Thread.Sleep(1000);//Wait 1 second
    Console.WriteLine("Task Status:" + task.Status);//Canceled

  

Usage scenario 2

Sometimes, we want to execute some code functions after triggering a certain time, but in an asynchronous environment, we can't guarantee whether the code to be executed is ready. For example, we have a Close method. When we call Close, it means that it is closed. If we execute some notifications when the program is closed, generally, we may think of adopting the event model, Either pass in the event delegate in the Close method, or adopt some models such as template design to implement:

    class Demo
    {
        public void Close(Action callback)
        {
            //close
            Thread.Sleep(3000);

            callback?.Invoke();//Execution notice
        }
    }

Or

    class Demo
    {
        public event Action Callback;

        public void Close()
        {
            //close
            Thread.Sleep(3000);

            Callback?.Invoke();//Execution notice
        }
    }

However, there is a problem. If the parameter is passed in or the event model is adopted, because as mentioned earlier, if in an asynchronous environment, we can't guarantee whether the code to be executed is ready. Maybe the program hasn't registered a callback when executing the Close method.

You can use the cancellation token to solve this problem:

    class Demo
    {
        CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

        public CancellationToken Token { get => cancellationTokenSource.Token; }

        public void Close()
        {
            //close
            Thread.Sleep(3000);

            cancellationTokenSource.Cancel();//Execution notice
        }
    }

The master needs to register a callback in the Token attribute without paying attention to when Close is executed

 

Usage scenario 3

Sometimes, we write an asynchronous infinite loop method to deal with some problems, and we hope to stop it outside the method. At this time, we can realize it by returning CancellationTokenSource:

        public CancellationTokenSource Listen()
        {
            CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();

            //Circular scheduling execution
            Task.Run(() =>
            {
                while (true)
                {
                    cancellationTokenSource.Token.ThrowIfCancellationRequested();

                    //Loop to perform some operations
                    Thread.Sleep(1000);
                    Console.WriteLine("Run"); } });
return cancellationTokenSource; }

 

Tags: C#

Posted by Terminator on Tue, 24 May 2022 16:39:48 +0300