Cyber-RT系列之协程Croutine

Catalogue
  1. 1. 前言
  2. 2. Cyber/croutine目录
  3. 3. Croutine类图
  4. 4. 结构图
  5. 5. 协程的创建
    1. 5.1. Why RoutineFactory?
    2. 5.2. What is RoutineFactory?
  6. 6. 重要过程时序流图

前言

协程是Cyber-RT的实现任务轮转的最小单位,是处理数据回调的运行模块。协程可以理解为“可以暂停”的函数,相比于线程,其具有中断可恢复的特性,那么只需要在开一个全局的数组存储所有的协程,在协程中断时,不断轮转调用下一个协程继续运行即可达到类似线程的效果。

为什么选用协程,因为基于协程的特性再加上Cyber中枢调度Scheduler的线程调度,可以避免回调时由于阻塞导致其他回调不能被执行的情况;此外,协程是在用户态来完成上下文切换的,所以切换耗时只有区区100ns多一些,比进程切换要高30倍。

Cyber/croutine目录

1
2
3
4
5
6
7
8
9
10
├── BUILD
├── CMakeLists.txt
├── croutine.cc
├── croutine.h
├── detail
│ ├── routine_context.cc
│ ├── routine_context.h
│ ├── swap_aarch64.S
│ └── swap_x86_64.S
└── routine_factory.h

Croutine类图

...

结构图

协程的创建

Cyber-RT中有两个地方创建了协程:

  • Component的初始化
  • Reader的初始化

两处地方的创建大同小异,这里给出Component<M0, NullType, NullType, NullType>::Initialize函数中关于创建协程的代码片段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
template <typename M0>
bool Component<M0, NullType, NullType, NullType>::Initialize(

...

// lambda函数 (这个后面作为)
auto func = [self](const std::shared_ptr<M0>& msg) {
// 处理msg
...
};

...

// 构造 data::DataVisitor
auto dv = std::make_shared<data::DataVisitor<M0>>(conf);
// 创建协程工厂
croutine::RoutineFactory factory =
croutine::CreateRoutineFactory<M0>(func, dv);
auto sched = scheduler::Instance();
return sched->CreateTask(factory, node_->Name());

其流程大概是这样:

  1. 写出一个lambda函数,内部进行msg的回调处理
  2. 构造一个DataVistor
  3. 将lambda函数和创建的DataVistor一并作为参数,用于创建协程工厂croutine::RoutineFactory
  4. 将协程工厂作为参数,调用中枢调度Scheduler::CreateTask(...)函数

Why RoutineFactory?

看到上面的代码片段可能会觉得奇怪,为什么需要协程工厂这一层,直接创建一个协程不好吗?

这其实是一种封装,假设我们直接创建一个协程,那么需要传入一个回调函数,用于处理msg,然而Cyber-RT一共考虑了1-4种msg的情况,那么创建协程时传入的回调函数的参数就有4种情况,这使得接口不能统一。使用协程工厂就是为了对4种情况进行封装,并统一接口,即创建协程时只需要把协程工厂传入作为参数即可。

What is RoutineFactory?

重要过程时序流图