Unity 已可使用 Thread、Task 等处理多线程任务,但缺少成熟的多线程任务队列工具,所以在此实现一个,代码已上传 Git 项目 GRUnityTools,可直接下载源码或通过 UPM 使用
本文原地址:Unity实践—多线程任务队列实现
实现目标
- 串行与并发队列
队列是首要实现目标,且需要串行与并发两种队列,以覆盖不同需求
2. 同步与异步执行
因任务队列过多可能阻塞主线程,所以除同步执行外还需要多线程异步操作
3. 主线程同步
因为有多线程,但 Unity 部分操作只能在主线程执行,所以还需要线程同步到主线程
实现方式
- Task
Task 为当前 .Net 提供的实用性最高的多线程接口,可实现任务的监控与操纵
2. TaskScheduler
Task 专用调度器,可更便捷地实现 Task 队列调度
3. Loom
Loom 为网络上广为流传的 Unity 中调用主线程的工具类,目前找不到源码最原始地址,代码拷贝自知乎
实现过程
方案选择
最初即决定使用 Task 作为队列基本单位,但完全没有考虑 TaskScheduler
。原计划手动实现一个调度器,负责保存传入的 Task 放入队列,可设置同步异步,根据设置实现对队列的不同操作。后来再研究微软官方文档时发现在其 Task 文档的示例中有一个 LimitedConcurrencyLevelTaskScheduler
的演示代码,直接通过 TaskScheduler
实现了可控并发数量的调度器,且当设置并发数为1时队列中的任务会逐一按顺序执行即产生了串行队列效果
TaskScheduler
有两种使用方式
方式一:为 TaskFactory
配置 TaskScheduler,通过 TaksFactory
使用配置的调度器启动 Task
//创建并发数32的调度器
LimitedConcurrencyLevelTaskScheduler scheduler = new LimitedConcurrencyLevelTaskScheduler(32);
//方式1
TaskFactory factory = new TaskFactory(scheduler);
factory.StartNew(()=>{
//执行任务
});
方式二:直接使用 Task.Start(TaskFactory)
方法
//创建并发数1的调度器(此时即为串行队列效果)
LimitedConcurrencyLevelTaskScheduler scheduler = new LimitedConcurrencyLevelTaskScheduler(1);
//声明一个 Task 对象
Task task = new Task(()=>{
//任务
});
//启动 Task 指定调度器
task.Start(scheduler);
编写源码
创建名为 TaskQueue
的类,添加变量
//根据需求设置默认并发数
private const int DefaultConcurrentCount = 32;
//线程锁
private static object _lock = new object();
//默认静态串行队列对象
private static TaskQueue _defaultSerial;
//默认静态并发队列对象
private static TaskQueue _defaultConcurrent;
//持有的调度器
private LimitedConcurrencyLevelTaskScheduler _scheduler;
//提供默认串行队列
public static TaskQueue DefaultSerailQueue
{
get
{
if (_defaultSerial == null)
{
lock (_lock)
{
if (_defaultSerial == null)
{
_defaultSerial = new TaskQueue(1);
}
}
}
return _defaultSerial;
}
}
//提供默认并发队列
public static TaskQueue DefaultConcurrentQueue
{
get
{
if (_defaultConcurrent == null)
{
lock (_lock)
{
if (_defaultConcurrent == null)
{
_defaultConcurrent = new TaskQueue(DefaultConcurrentCount);
}
}
}
return _defaultConcurrent;
}
}
提供快捷构造方法
//默认构造方法,因 Loom 为 UnityEngine.Monobehaviour对象,所以必须执行初始化方法将其加入场景中
public TaskQueue(int concurrentCount)
{
_schedu