编写目的: unity官方是不推荐在unity里面使用多线程的,因为unity自身提出了协程的概念,但是unity中的协程并不是严格意义上面的多线程,它只是在特定时间段执行特定业务逻辑,达到多线程的功能,但是本质还是在主线程中完成的。当涉及复杂运算和复杂渲染等结合时就会出现卡顿等现象,所以我们可以另外开线程去执行负责业务逻辑,主线程负责渲染,这样就可以保证主线程的流畅性,提升游戏的体验感。
核心思路:网上含有比较多的线程管理方案,最简单有效的是Loom工具,而以下多线程管理的实现也是基于Loom工具改进优化而来。核心思想就是两个:
一.主线程的回调操作交给Update每帧调用来进行执行。
二:子线程的回调就由C#的ThreadPool动态获取一个没用的线程进行执行,由于ThreadPool并没有设置线程上限,所以需要通过变量控制线程数,并对变量进行原子锁加减操作,超过上限的调用处于等待锁状态。
主线程核心代码:
// 执行主线程操作
public void RunMainThread(Action action, double time = 0f) {
if (IsMainThread () && time == 0f) {
action ();
} else {
lock(action_list) {
action_list.Add(new DelayAction { time = GetMillisecond() + time, action = action});
}
}
}
private void RunMainAction() {
lock(action_list) {
action_execute_list.Clear();
action_execute_list.AddRange(action_list.Where(d=>d.time <= GetMillisecond()));
foreach (var item in action_execute_list) {
action_list.Remove(item);
}
}
foreach(var delayed in action_execute_list) {
delayed.action();
}
}
子线程核心代码:
public Thread RunThread(Action action, bool force = false) {
while(num_thread >= max_thread && !force) {
Thread.Sleep(1);
}
Interlocked.Increment(ref num_thread);
ThreadPool.QueueUserWorkItem(RunAction, action);
return null;
}
private void RunAction(object action) {
try {
((Action)action)();
} catch {
} finally {
Interlocked.Decrement(ref num_thread);
}
}
注意:
1.子线程中不能对unity的Object对象进行直接操作,需要交给主线程操作。
2.主线程的操作是交给Update中执行的,而Update又是每帧调用,所以延迟Action调用时可能不是很实时,但是最多不会偏差多与一帧。
3.子线程中如果强制执行时,就会直接执行,超过线程池上限就会再动态获取一个,但是线程上限不变,其他超过上限的线程继续处于等待状态,直到线程池中线程上限以下可以有用线程才会结束等待并分配线程执行。