stackless or stackfull 协同程式(协程)?

这是一个被多数人们喋喋不休,争论探索的问题,有人说 stackless 协同程式更好性能更快,对内存的使用率更好,似乎都是正确的。

对于我来说,我并不认为 stackfull 协同程式的效率就会低于 stackless 协同程式,所有的数据都应该进行过实际的测试在下结论,或许会更好且有足够的说服力。

C++ 开发人员们,或许更应当抱有更为严谨且专业的态度来审视这个问题,如果我们只是从代码上推导就轻易下决断是一种错误的做法。

stackless 协同程式在 C/C++ 语言上面有多种可行实现,来自 boost 基础框架类库提供的,C/C++ 20 语言标准集成提供的 stackless 协程。

本人没有深度使用过 C/C++ 语言的 stackless 协同程式,毕竟 C/C++ 领域的工程师们,几乎都不乐于追求及应用新潮流技术。

C# 语言提供的 stackless 的协同程式,于 .NET 4.5 Framework 时代被引入,在之前 C# 语言实现协同程式是一种基于早前制定的 C# 语言 yield 规范标准来实现的。

主流的 stackless 的协同程式分为两种实现形式,探索 stackless 的技术原理远比仅仅只会使用它更为重要。

stackless 是由编译器实现的,它本质上是一种多层嵌套的匿名函数及结构,人们访问的变量被存放在具体的结构内。

Yield 函数由驱动器(或者说运行时)调用,每个中断点(Yield)均为一个 Step(步骤),例如(伪代码):

void do() {

// 步骤一

yield return 1;

// 步骤二

yield return 2;

}

此处只是举个小例字,准确的说它是一个状态机结构,结构体字段存储工作时所需的变量,C++ 20 语言标准的 co_await/co_yield 组成的 stackless 协同程式工作原理是类似的。

但我并不关心这一切,stackless 协同程式工作的效率快不快,取决于程式如何去驱动它执行对应的工作流。

例如:我们将 stackless 协同程式的实例(可以理解为一个线程对象)进行工作时,驱动器是一个在线程类循环队列来步进协同程式的工作,可能系统整体的吞吐效能是很低的。

void scheduler_update() {

for (auto& coroutine : coroutines)  {

if (coroutine.state == Coroutine::kRuning) {

coroutine.Next();

}

else {

...

}

}

}

如上述的驱动形式,程式执行效能就比较低下,如果我们为每个分配的协同程式串上链表,当协同程式处理 Yield 时,检查并尝试向 Next 节点进行切换到下一个协同程式,那么效率就会比较高,当然本人只是简单的聊聊,不会涉及到深入的知识点。

void scheduler_execute(auto& coroutine ) {

if (coroutine.state == Coroutine::kRuning) {

coroutine.Next();

}

}

auto* coroutine = ...;

do {

...

scheduler_execute(*coroutine);

coroutine = coroutine->Next; 

...

} while (coroutine);

但,我仍并不建议这样去驱动协同程式,绝大多数应用协同程式的场景都在 Asynchronous(异步的)访问资源处理。

人们是否可以在一个异步的操作被发起时,挂起协同程式,在完成异步的时候恢复协同程式的工作,这样可以省却不必要的开销,大大的提高程式的工作效能。

对于我来说,我并不反感使用 stackless 还是 stackful 协同程式,两者各有优缺点,如果非要说更好的解决方案,或许由编译器复杂实现并支持的 stackless 协同程式是相对最佳的方案,但这里也涉及到,人们会如何去驱动这些协同程式工作。

stackless 协同程式吞吐效能潜在的最大问题是:它是如何驱动这个协同程式进行工作的?如果它是由用户手动在合适的切入点进行驱动的,效能的确是比 stackful 协同程式需要备份并且还原所需 ESP/EBP/EDX/ECX/FS 等寄存器值后JUMP过去的效率要高一些,但这里仅限于假设 stackful 协同程式需要做类似这样的行为。

stackful、stackless 协同程式是两种具有代表性的 coroutine 解决方案,但协同程式切换不仅仅只有这两种解决方案,state-threads (状态线程)的协程切换效率比两者都要高不少,但带来的结果是用户编写的 C/C++ 工程代码晦涩难懂,难以被人维护。

但大多数的 stackless 协同程式执行效能并不一定比 stackful 协同程式的效能更好,因为 stackless 协同程式实现的很多方案内会增加其它一些不必要的东西,比如非必须的运行时用于支持语言平台用户程式很难所使用的功能需求,而牺牲了一定效能。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Stackless是一个Python库,它提供了协程(coroutine)支持,可以在单个线程中实现并发执行。它是一个轻量级的替代方案,可以避免使用线程和进程所带来的开销。在本教程中,我们将介绍如何使用Stackless来实现协程。 安装Stackless库 Stackless库可以通过pip安装,打开终端并执行以下命令: ``` pip install stackless ``` 创建协程 要创建一个协程,我们需要使用stackless.tasklet()函数。该函数将一个函数作为参数,并返回一个tasklet对象。在下面的示例中,我们将定义一个简单的函数,该函数将打印一条消息: ```python import stackless def print_message(message): print(message) tasklet = stackless.tasklet(print_message) ``` 启动协程 要启动协程,我们需要使用tasklet.run()函数。该函数将在当前线程中启动协程。在下面的示例中,我们将启动上面定义的协程: ```python tasklet.run("Hello, world!") ``` 在上面的示例中,我们将“Hello, world!”作为参数传递给tasklet.run()函数,该参数将作为print_message()函数的参数传递。当我们运行这个程序时,它将输出“Hello, world!”。 切换协程 Stackless库允许我们在协程之间切换。要切换到另一个协程,我们可以使用stackless.schedule()函数。下面的示例演示了如何在两个协程之间切换: ```python import stackless def print_message(message): print(message) stackless.schedule() tasklet1 = stackless.tasklet(print_message) tasklet2 = stackless.tasklet(print_message) tasklet1.run("Hello from tasklet1!") tasklet2.run("Hello from tasklet2!") ``` 在上面的示例中,我们定义了两个协程tasklet1和tasklet2。我们启动这些协程,并在每个协程中打印一条消息。在每个协程中,我们使用stackless.schedule()函数将控制权传递给另一个协程。当我们运行这个程序时,它将交替输出“Hello from tasklet1!”和“Hello from tasklet2!”。 关闭协程 关闭协程时,我们可以使用tasklet.kill()函数。该函数将终止协程并释放其资源。下面的示例演示了如何关闭一个协程: ```python import stackless def print_message(message): print(message) tasklet = stackless.tasklet(print_message) tasklet.run("Hello, world!") tasklet.kill() ``` 在上面的示例中,我们定义了一个协程tasklet。我们启动此协程并打印一条消息。然后,我们使用tasklet.kill()函数关闭该协程。当我们运行这个程序时,它将输出“Hello, world!”并立即退出。 总结 在本教程中,我们介绍了如何使用Stackless库来实现协程。我们学习了如何创建和启动协程,如何在协程之间切换,以及如何关闭协程Stackless是一个非常有用的库,可以帮助我们实现高效的并发执行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值