今天儿童节,犬子已经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);
}