Unity异步编程【6】——Unity中的UniTask如何取消指定的任务或所有的任务

今天儿童节,犬子已经9个多月了,今天是他的第一个儿童节。中年得子,其乐无穷(音:ku bu kan yan)…回头是岸啊

〇、 示例效果

一连创建5个异步任务[id 从0~4],先停止其中的第id == 4的任务,再停止所有的任务
请添加图片描述

一、CancellationTokenSource有什么用?

在Unity中,CancellationTokenSource用于创建和控制CancellationToken实例,以便在需要时请求取消异步操作。CancellationToken实例是一种轻量级的结构,用于在异步操作执行期间检查是否已请求取消。

首先,您需要创建一个CancellationTokenSource实例,用于创建和控制CancellationToken实例。然后,将CancellationToken实例传递给您要执行的异步任务,以便在需要时请求取消操作。最后,您可以调用CancellationTokenSource实例的Cancel()方法来请求取消所有使用该CancellationToken的异步操作。

二、cts用法示例

  • 1、定义一个异步任务——它的参数为CancellationToken
public async UniTask FlowAsync(CancellationToken ctk)
    {
        foreach (var script in scripts)
        {
            Debug.Log($"执行步骤:{script.name}");
            await (script as IFlowAsync).FlowAsync(ctk);
        }
    }
  • 2、启动一个cts任务,用于管理FlowAsync任务
    生成一个 cts 实例,TaskSingol的定义见下文《用多个CancellationTokenSource实例来管理多个异步任务管理》
#if UNITY_EDITOR
    [ContextMenu("测试步骤")]
#endif
    void testAsync()
    {
        var ctsInfo = TaskSingol.CreatCts(); //生成一个 cts 实例,TaskSingol的定义见下文《用多个CancellationTokenSource实例来管理多个异步任务管理》
        FlowAsync(ctsInfo.cts.Token);        //传入token  
    }

三、用多个CancellationTokenSource实例来管理多个异步任务管理

  • 定义一个全局的class,用于管理不同异步的cancellation操作
  • 每次启动一个异步任务,都对应一个CancellationTokenSource实例和一个task id
  • 包含功能:
    1、创建一个异步任务
    2、取消所有异步任务
    3、取消指定的异步任务
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());
    }
}

四、凡是异步方法都传入cancellationToken,凡是await的地方,都设置cancellationToken

1、 任务A的monobehaviour脚本

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、 任务B的monobehaviour脚本


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、测试脚本

测试内容:
启动一个任务
停止所有任务
停止指定任务【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、如何让所有的脚本都实现一个FlowAsync的方法——实现一个接口

如下所示,FlowA脚本继承了MonoBehaviour类,还实现了IFlowAsync的接口
下面是FlowA 的脚本关键代码,那么如何定义一个接口Class呢,见下 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、如何让所有的脚本都实现一个FlowAsync的方法——定义一个接口

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

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

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>