Hacker News 中文摘要

RSS订阅

将协程融入C语言 -- Hacking Coroutines into C

文章摘要

文章探讨了在嵌入式软件开发中使用状态机的局限性,并提出了一种替代方案——协程。作者在开发过程中发现,传统的状态机虽然有效,但代码的可读性和维护性较差,缺乏线性流程。由于项目限制无法使用实时操作系统(RTOS),作者受到其他语言中协程的启发,认为协程可以在不依赖线程的情况下实现类似的多任务处理,从而简化控制流,提升代码的清晰度。

文章总结

文章《Hacking Coroutines into C》主要探讨了如何在C语言中实现协程(coroutines),以解决嵌入式开发中状态机的复杂性问题。作者在开发嵌入式软件时,面对大量状态机的维护和理解困难,提出了使用协程来简化控制流的想法。

主要内容总结:

  1. 问题背景

    • 作者在开发嵌入式软件时,使用了大量的状态机来管理控制流。虽然状态机在嵌入式开发中很常见,但它们的非线性特性使得代码难以理解和维护。
    • 作者希望找到一种更清晰的方式来表达控制流,类似于顺序执行的程序,能够在等待事件时暂停并在事件发生后继续执行。
  2. 协程的引入

    • 协程是一种可以在执行过程中暂停和恢复的机制,类似于协作式多任务处理。作者在Python、JavaScript、Dart和Rust等语言中使用过协程,认为它可以在没有操作系统的情况下实现并发。
    • 作者决定在C语言中实现协程,以替代传统的状态机。
  3. 示例:LED闪烁控制

    • 作者通过一个LED闪烁的示例来说明状态机的复杂性。LED的闪烁周期可以通过按钮控制,按钮按下时记录时间,按钮释放时计算新的闪烁周期。
    • 使用状态机实现该功能时,代码变得复杂且难以理解。
  4. 协程的实现

    • 作者通过宏(macros)在C语言中实现了协程。宏将协程转换为显式的状态机,每个协程的执行点通过状态变量来管理。
    • 协程的实现类似于Duff's Device,使用switch语句和状态变量来模拟协程的暂停和恢复。
  5. 协程调度器

    • 作者实现了一个简单的协程调度器(coro_executor),负责管理协程的调度、暂停和恢复。调度器通过队列管理协程任务,并在每次循环中处理这些任务。
  6. 协程的取消机制

    • 协程可以通过canceled标志来取消执行。每个协程在运行时检查该标志,如果被取消则提前退出。
  7. 条件变量的实现

    • 作者还实现了条件变量(corocondvar),用于在协程之间进行通信。条件变量通过链表管理等待的协程,并在信号发出时唤醒它们。
  8. 最终实现

    • 作者将LED闪烁和按钮记录的协程结合起来,展示了如何在Arduino环境中运行这些协程。通过调度器,协程可以在单线程环境中实现协作式多任务处理。
  9. 总结与反思

    • 作者承认这种实现方式虽然巧妙,但并不适合生产环境。现代编程语言如Rust已经原生支持协程和异步编程,提供了更安全、更高效的解决方案。
    • 作者还提到了Adam Dunkels的Protothreads,这是一种更优雅的协程实现方式,使用__LINE__宏来表示状态,进一步简化了协程的实现。

图片标记:

  • Image 1: Statemachine

最终结论:

虽然这种C语言中的协程实现方式在技术上很有趣,但现代编程语言如Rust已经提供了更优雅、更安全的异步编程解决方案。对于需要并发处理的嵌入式系统,建议使用更现代的工具和语言。

评论总结

  1. FreeRTOS与协作调度器

    • mikepurvis提到FreeRTOS可以使用协作调度器,并建议使用“真正的”代码生成而不是宏,以提高代码的可读性和调试性。
    • 引用:“I’d prefer to try to do it with ‘real’ codegen than macros.”
    • 引用:“you’d get much more readable resulting source when it came to stepping through it with a debugger.”
  2. 状态机的复杂性

    • Neywiny认为状态机在软件中过于复杂,尤其是在处理顺序执行时,建议直接编写长函数而不是过度拆分。
    • 引用:“I historically hate state machines for sequential executioners.”
    • 引用:“Sometimes, you just need to do a lot of work.”
  3. Protothreads的相似性

    • throwaway81523指出文章中的方法与Protothreads类似,并批评了文章的CSS设计。
    • 引用:“this is sort of like protothreads which has been around for ages.”
    • 引用:“The article’s CSS was so awful that I didn’t read anything except the last paragraph.”
  4. RTOS的替代方案

    • userbinator认为不使用RTOS的项目最终会实现类似的机制,并建议使用PWM或定时器中断来简化问题。
    • 引用:“That tends to just make the project eventually implement an approximation of one.”
    • 引用:“I suspect that approach is even simpler than what’s shown here.”
  5. 轻量级Protothreads的实现

    • adinisom分享了一种在C语言中实现轻量级Protothreads的技巧,强调其简洁性和灵活性。
    • 引用:“My favorite trick in C is a light-weight Protothreads implemented in-place without dependencies.”
    • 引用:“What’s especially nice that I miss in other languages with async/await is ability to mix declarative and procedural code.”
  6. C语言中的协作多线程

    • Nursie提到C语言中通过setjmp和longjmp实现协作多线程的技术已有多年历史,但逐渐被遗忘。
    • 引用:“Cooperative multithreading via setjmp and longjmp has been around in C since the 80s at least.”
    • 引用:“Perhaps it’s almost becoming lost knowledge :)”
  7. 同步语言的类似方法

    • syncurrent提到一种基于同步语言(如Esterel或Blech)的类似方法。
    • 引用:“A similar approach, but rooted in the idea of synchronous languages like Esterel or Blech.”
  8. 状态机的可视化与模块化

    • astrobe_讨论了状态机的非线性和并发问题,强调可视化工具的重要性,并指出状态机的模块化难度。
    • 引用:“One issue with state machines is that they are hardly modular.”
    • 引用:“Seeing what’s going on under the hood is more important that one usually thinks.”
  9. 手动更新堆栈的实现

    • moconnor提到同事通过手动更新堆栈和跳转实现类似功能,且代码至今仍在使用。
    • 引用:“A colleague of mine did this much more elegantly by manually updating the stack and jmping.”
  10. C语言中协程的复杂性

    • user__name认为在C语言中实现协程过于复杂,建议使用语言级别的支持。
    • 引用:“Honestly I feel like you need language level support for them in that case.”
    • 引用:“they seem more trouble than they’re worth otherwise, at least in plain C.”
  11. Rust的异步执行

    • joshlk提到Rust在嵌入式环境中提供内置的异步执行支持。
    • 引用:“Rust can be used in an embedded environment and also offers asynchronous execution built into the language.”