UE4的Delegate还可以绑定lambda,是很好用的事件系统,可以通过它来实现模块间的脱藕。但是使用Delegate可能引发回调嵌套,导致代码难以阅读及维护。而通过boost.coroutine2可以实现以同步的方式编写异步逻辑,从而避免回调嵌套。
coroutine简介:
协程可以很轻量的在子例程中进行切换,它由程序员进行子例程的调度(即切换)而不像线程那样需要内核参与,同时也省去了内核线程切换的开销,因为一个协程切换保留的就是函数调用栈和当前指令的寄存器,而线程切换需要陷入内核态,改变线程对象状态。
协程分为对称协程(symmetric)和非对称协程(asymmetric),对称协程需要显式指定将控制权yeild给谁,非对称协程可以隐式的转移控制权给它的调用者,boost coroutine2实现的是非对称协程。最简单的协程可以这样:
#include <iostream>
#include <boost/coroutine/all.hpp>
typedef boost::coroutines::asymmetric_coroutine< void >::pull_type pull_coro_t;
typedef boost::coroutines::asymmetric_coroutine< void >::push_type push_coro_t;
void foo(push_coro_t & sink)
{
std::cout << "1";
sink();
std::cout << "2";
sink();
std::cout << "3";
sink();
std::cout << "4";
}
int main(int argc, char * argv[])
{
{
pull_coro_t source(foo);
while (source)
{
std::cout << "-";
source();
}
}
std::cout << "\nDone" << std::endl;
return 0;
}
运行输出:
1-2-3-4
Done
与UE4结合的思路:
1. 需要在回调完成之后才能执行的逻辑放在sink()之后,这样代码书写是同步的,但在回调完成之前该逻辑会被sink提前排斥掉,故不会执行同时也不会阻塞主线程。
2. 每次Tick执行步骤1,当检测到回调已完成时,回调之后执行的逻辑才会执行。
3. 1个或多个回调及其后执行的逻辑可以封装为一个Task,全局持有Task列表,在Tick里遍历执行每个Task,不可用的Task则移出列表。
4. 每个Task封装一个循环检测回调是否完成的接口,若回调全部完成则退出循环不触发sink();否则触发sink(),这样下次Tick时仍然会执行到该循环体内,继续检测。