Unity asynchronous programming [6] - how UniTask in Unity cancels the specified task or all tasks

Today is Children's Day, Quanzi has been more than 9 months old, and today is his first Children's Day. Having a child in middle age is a lot of fun (sound: ku bu kan yan)...it's a good time to turn back

〇. Example effect

Create 5 asynchronous tasks [id from 0 to 4] in a row, first stop the task whose id == 4, and then stop all tasks
Please add a picture description

1. What is the use of CancellationTokenSource?

In Unity, CancellationTokenSource is used to create and control CancellationToken instances to request cancellation of asynchronous operations when needed. A CancellationToken instance is a lightweight structure used to check whether cancellation has been requested during the execution of an asynchronous operation.

First, you need to create a CancellationTokenSource instance, which is used to create and control CancellationToken instances. Then, pass the CancellationToken instance to the async task you want to execute to request cancellation if needed. Finally, you can call the Cancel() method of the CancellationTokenSource instance to request cancellation of all asynchronous operations using that CancellationToken.

2. Examples of cts usage

  • 1. Define an asynchronous task - its parameter is CancellationToken
public async UniTask FlowAsync(CancellationToken ctk)
    {
    
    
        foreach (var script in scripts)
        {
    
    
            Debug.Log($"执行步骤:{
      
      script.name}");
            await (script as IFlowAsync).FlowAsync(ctk);
        }
    }
  • 2. Start a cts task to manage FlowAsync tasks.
    Generate a cts instance. For the definition of TaskSingol, see the following "Using Multiple CancellationTokenSource Instances to Manage Multiple Asynchronous Task Management"
#if UNITY_EDITOR
    [ContextMenu("测试步骤")]
#endif
    void testAsync()
    {
    
    
        var ctsInfo = TaskSingol.CreatCts(); //生成一个 cts 实例,TaskSingol的定义见下文《用多个CancellationTokenSource实例来管理多个异步任务管理》
        FlowAsync(ctsInfo.cts.Token);        //传入token  
    }

3. Use multiple CancellationTokenSource instances to manage multiple asynchronous task management

  • Define a global class for managing different asynchronous cancellation operations
  • Each time an asynchronous task is started, it corresponds to a CancellationTokenSource instance and a task id
  • Contains functions:
    1. Create an asynchronous task
    2. Cancel all asynchronous tasks
    3. Cancel the specified asynchronous task
using System.Collections.Generic;
using UnityEngine;
using System.Threading;
using System;
using System.Linq;

/// <summary>
/// 任务信号
/// </summary>
public static class TaskSingol
{
    
    
    /// <summary>
    /// 任务信息
    /// </summary>
    [Serializable]
    public class CtsInfo
    {
    
    
        /// <summary>
        /// 任务id
        /// </summary>
        [SerializeField]
        public int id;

        /// <summary>
        /// cst实例
        /// </summary>
        [SerializeField]
        public CancellationTokenSource cts;
    }

    /// <summary>
    /// 任务池子
    /// </summary>
    public static List<CtsInfo> ctsInfos = new List<CtsInfo>();

    /// <summary>
    /// 任务编号【自增】
    /// </summary>
    private static int id = 0;

    /// <summary>
    /// 创建一个任务
    /// </summary>
    /// <returns></returns>
    public static CtsInfo CreatCts()
    {
    
    
        var cts = new CancellationTokenSource();
        var ci = new CtsInfo{
    
    cts = cts,id = id};
        id++;
        ctsInfos.Add(ci);
        return ci;
    }

    /// <summary>
    /// 取消所有的任务
    /// </summary>
    public static void CancelAllTask()
    {
    
    
        ctsInfos.ForEach(ci=>ci.cts.Cancel());
    }

    /// <summary>
    /// 取消指定的任务
    /// </summary>
    public static void CancelTask(int id)
    {
    
    
        ctsInfos.Where(ci=>ci.id == id).ToList().ForEach(ci => ci.cts.Cancel());
    }
}

4. CancellationToken is passed in to all asynchronous methods, and cancellationToken is set in all await places

1. Monobehaviour script for task A

using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using Cysharp.Threading.Tasks;

public class FlowA : MonoBehaviour, IFlowAsync
{
    
    
    public async UniTask FlowAsync(CancellationToken ctk)
    {
    
    
        Debug.Log($"我是monobehaviourA {
      
      Time.realtimeSinceStartup}");
        await UniTask.Delay(2000,cancellationToken:ctk);
        Debug.Log($"UniTask.Delay(2000) {
      
      Time.realtimeSinceStartup}");
    }
}

2. Monobehaviour script for task B


using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using Cysharp.Threading.Tasks;

public class FlowB : MonoBehaviour, IFlowAsync
{
    
    
    public async UniTask FlowAsync(CancellationToken ctk)
    {
    
    
        Debug.Log($"我是monobehaviourB {
      
      Time.realtimeSinceStartup}");
        await UniTask.Delay(2000,cancellationToken:ctk);
        Debug.Log($"UniTask.Delay(2000) {
      
      Time.realtimeSinceStartup}");
    }
}

3. Test script

Test content:
start a task,
stop all tasks,
stop specified tasks【4】

using System;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;
using System.Threading.Tasks;
using Cysharp.Threading.Tasks;

public class TestTask : MonoBehaviour
{
    
    
    private int idx;

#if UNITY_EDITOR
    [ContextMenu("启动一个任务")]
#endif
    void test()
    {
    
    
        var ctsInfo = TaskSingol.CreatCts();
        RunTask(ctsInfo.id, ctsInfo.cts.Token);
    }

#if UNITY_EDITOR
    [ContextMenu("停止所有任务")]
#endif
    void test2()
    {
    
    
        TaskSingol.CancelAllTask();
        Debug.Log("停止所有的任务");
    }

#if UNITY_EDITOR
    [ContextMenu("停止所有任务【4】")]
#endif
    void test3()
    {
    
    
        TaskSingol.CancelTask(4);
        Debug.Log("停止任务4");
    }

    public async UniTask RunTask(int taskIndex,CancellationToken ctk)
    {
    
    
        Debug.Log($"{
      
      taskIndex}:任务运行中,1/4等待五秒");
        await UniTask.Delay(TimeSpan.FromSeconds(5),cancellationToken:ctk);
        Debug.Log($"{
      
      taskIndex}:任务运行中,2/4等待五秒");
        await UniTask.Delay(TimeSpan.FromSeconds(5), cancellationToken: ctk);
        Debug.Log($"{
      
      taskIndex}:任务运行中,3/4等待五秒结束,再等5秒");
        await UniTask.Delay(TimeSpan.FromSeconds(5), cancellationToken: ctk);
        Debug.Log($"{
      
      taskIndex}:任务运行中,4/4等待五秒结束,再等20秒");
        await UniTask.Delay(TimeSpan.FromSeconds(20), cancellationToken: ctk);
    }
}

4. How to make all scripts implement a FlowAsync method - implement an interface

As shown below, the FlowA script inherits the MonoBehaviour class, and also implements the interface of IFlowAsync.
The following is the key code of the FlowA script. How to define an interface Class? See next 5

public class FlowA : MonoBehaviour, IFlowAsync
{
    
    
    public async UniTask FlowAsync(CancellationToken ctk)
    {
    
    
        Debug.Log($"我是monobehaviourA {
      
      Time.realtimeSinceStartup}");
        await UniTask.Delay(2000,cancellationToken:ctk);
        Debug.Log($"UniTask.Delay(2000) {
      
      Time.realtimeSinceStartup}");
    }
}

5. How to make all scripts implement a FlowAsync method - define an interface

using System.Threading;
using Cysharp.Threading.Tasks;

/// <summary>
/// 接口:定义一个叫FlowAsync的异步方法
/// </summary>
public interface IFlowAsync
{
    
    
    public UniTask FlowAsync(CancellationToken ctk);
}

Guess you like

Origin blog.csdn.net/dzj2021/article/details/130990440