【计算机基础】解读协程库(重新认识一下协程、协程的状态、创建协程的结构体)

📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨
📢:不妨浏览一下我的博客主页【https://blog.youkuaiyun.com/weixin_51244852
📢:文章若有幸对你有帮助,可点赞 👍 收藏 ⭐不迷路🙉
📢:内容若有错误,敬请留言 📝指正!原创文,转载请注明出处


什么是协程

协程,英文Coroutines,是一种比线程更加轻量级的存在
1、协程就是一个可以在某个地方挂起的特殊函数,并且可以重新在挂起处继续运行。
2、一个线程内的多个协程的运行是串行的。
3、如果有多核CPU的话,多个进程或一个进程内的多个线程是可以并行运行的,但是一个线程内的多个协程却绝对串行的,无论有多少个CPU(核)。
4、一个线程内的多个协程是串行执行的,不能利用多核,所以,显然,协程不适合计算密集型的场景。协程适合I/O 阻塞型。
在这里插入图片描述

ProtoThread简介

ProtoThread是一个极简的C语言协程库,由5个简单的.h文件构成。ProtoThread主要是利用switch case内嵌循环的特殊语法来实现的。因为ProtoThread完全是利用C语言的语法特性,所以ProtoThread可以适用所有C/C++项目。我们创建并使用协程都要用到这个协程库,而这个库主要使用宏去定义协程。其次我们在定义的时候都使用PT来表示协程的意思。

.h文件介绍:

lc.h :local continuation,本地程序。以标准C的switch结构实现Protothreads系统。
pt.h :Protothreads的接口,必须包含此头文件。
pt-sem.h :附加的信号量操作的支持,不需要的话则不必包含他在pt.h中包含所有实现Protothreads的宏和变量结构的定义。
LC是local continuation,是Protothread机制的底层支持,用来保存进程运行状态的地方,其实就是保存进程实体函数上次阻塞的位置。
在这里插入图片描述

协程宏

协程宏是一种利用宏定义来简化协程的创建和管理的技术。在C语言中,可以使用宏来实现协程的简单调度。通过定义一组宏,可以将协程的创建、切换、暂停和恢复等操作以类似于语言级别的方式进行编写和使用。

定义协程的状态

这段代码是为一种协程实现定义了4个常量。

以下是对这些常量的解释:

#define PT_WAITING 0
#define PT_YIELDED 1
#define PT_EXITED  2
#define PT_ENDED   3
  • PT_WAITING 的值为 0,表示协程处于等待状态。
  • PT_YIELDED 的值为 1,表示协程已经暂停执行,并且可以被恢复继续执行。
  • PT_EXITED 的值为 2,表示协程已经完成执行并退出。
  • PT_ENDED 的值为 3,表示协程已经结束(可能是正常结束或异常结束)。

创建协程的结构体

struct pt {
  lc_t lc;
  unsigned short HeapSize;
  void *pHeap; 
};

这段代码定义了一个名为 pt 的结构体,其中包含了以下成员变量:

  • lc_t lc:一个类型为 lc_t 的变量,可能是用于保存协程执行状态的上下文或相关信息。
  • unsigned short HeapSize:一个无符号短整型变量,表示堆大小。
  • void *pHeap:一个指向 void 类型的指针,指向一个堆内存区域。

这个结构体的目的是在协程实现中保存和管理相关的数据。lc_t
可能是一个类型别名,用于表示协程的执行状态,例如使用汇编语言或特定库函数对协程进行处理。

HeapSizepHeap 成员变量用于处理堆内存的分配和管理,可能在协程执行过程中需要动态分配一些内存。

协程初始化

#define LC_INIT(s) s = 0;
#define PT_INIT(pt)   LC_INIT((pt)->lc)

这是一个宏定义,用于初始化一个名为pt的指针。
它调用了另一个宏LC_INIT,并将pt指向的对象的lc成员作为参数传递给LC_INIT

协程的开始、退出与结束

协程的开始

#define PT_BEGIN(pt) { char PT_YIELD_FLAG = 1; LC_RESUME((pt)->lc)

这是一个宏定义,用于开始一个结构体或类的定义。它首先将全局变量PT_YIELD_FLAG设置为1,然后调用LC_RESUME函数,将pt指向的对象的lc成员作为参数传递给该函数。
PT_BEGIN(pThread) 是一个用于启动协程(coroutine)的宏定义。它在使用 Protothreads 库时常见,并用于定义和管理协程的执行流程。

协程的启动对硬件或是固件有何影响吗?

PT_BEGIN(pThread) 被调用时,协程会开始执行,并且会在该宏定义内部的代码块中按顺序逐条执行。执行会一直持续到遇到 PT_END 宏定义或者遇到了 PT_WAIT_UNTILPT_WAIT_WHILEPT_WAIT_THREAD 等等可能导致协程暂停的语句。

具体地说,当协程启动后,以下情况可能发生:

  1. 如果协程内部没有任何暂停语句(如 PT_WAIT_UNTILPT_WAIT_WHILE),那么协程会立即执行完内部代码块中的所有语句,然后结束。
  2. 如果协程内存在暂停语句,协程的执行将会受到这些暂停语句的控制。当遇到一个暂停语句时,协程将暂停执行并返回到协程被调度的位置,等待满足暂停条件后再次恢复执行。

在整个协程执行过程中,fw(固件)或硬件可以根据开发者的代码逻辑做出相应的反应。例如,可以在协程的执行过程中与硬件进行通信、读取传感器数据、控制设备等。具体的反应取决于所编写的代码和相关的硬件或固件。

需要注意的是,协程的执行通常是非阻塞的,这意味着它可以在单个线程中实现多任务并发。通过合理地使用暂停语句,协程可以在不同的任务之间切换,使得看起来好像同时运行多个任务。这种方式可以提高系统的效率和响应性,并简化异步编程的实现。

总之,PT_BEGIN(pThread) 的调用将启动一个协程,并根据协程内部代码块中的逻辑执行相应的操作,包括与fw或硬件的交互,具体的效果取决于协程内部代码的实现。

协程的退出

#define PT_EXIT(pt)				\
  do {						\
    PT_INIT(pt);				\
    return PT_EXITED;			\
  } while(0)

协程的结束

#define LC_END(s) 
#define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \
                   PT_INIT(pt); return PT_ENDED; }

这段代码是一个宏定义,用于结束一个名为pt的指针。它首先调用了另一个宏LC_END,将pt指向的对象的lc成员作为参数传递给LC_END。然后,它将全局变量PT_YIELD_FLAG设置为0,表示不再产生yield。最后,它再次调用PT_INIT宏,初始化pt指针,并返回一个名为PT_ENDED的标志。

分配子线程的堆内存

PT_CHILD_HEAP_ALLOC
这段代码定义了一个宏 PT_CHILD_HEAP_ALLOC,用于在父线程的堆中分配子线程的堆内存。

#define PT_CHILD_HEAP_ALLOC(ptrCurrThread, CurrThreadVarStruct, ptrChildThrd)               \
do {                                                                                        \
    unsigned long  dTmp = ((sizeof(CurrThreadVarStruct) + 7) & (~(unsigned long)0x07));     \
    if ((ptrCurrThread)->HeapSize < dTmp)                                                   \
    {                                                                                       \
        COMM_ASSERT_TRUE(FALSE,"PT_ChildHeapSizeErr!");                                    \
    }                                                                                       \
    else                                                                                    \
    {                                                                                       \
        (ptrChildThrd)->pHeap = (void *)((unsigned char *)((ptrCurrThread)->pHeap) + dTmp); \
        (ptrChildThrd)->HeapSize = (ptrCurrThread)->HeapSize - dTmp;                        \
    }                                                                                       \
} while(0)

这个宏的作用是为子线程在父线程的堆上分配内存空间。

具体解释如下:

ptrCurrThread 是指向当前线程变量的指针。 CurrThreadVarStruct 是表示当前线程变量结构的类型。
ptrChildThrd 是指向子线程变量的指针。 在宏的实现中,先计算出 CurrThreadVarStruct 类型的大小加上 7
后按位取与 0x07 的结果,并赋值给 dTmp。这一步目的是将大小舍入到8的倍数,确保内存对齐。

然后,宏检查父线程的堆大小是否小于 dTmp。如果是,则产生一个断言错误。否则,将子线程的堆指针设置为父线程堆指针加上
dTmp,并将子线程的堆大小设置为父线程堆大小减去 dTmp。

在这里插入图片描述

orchid是一个构建于强大的boost基础上的C ,类似于python下的gevent/eventlet,为用户提供基于协程的并发模型。 协程,顾名思义,协作式程序,其思想是,一系列互相依赖的协程间依次使用CPU,每次只有一个协程工作,而其他协程处于休眠状态协程在控制离开时暂停执行,当控制再次进入时只能从离开的位置继续执行。 协程已经被证明是一种非常有用的程序组件,不仅被python、lua、ruby等脚本语言广泛采用,而且被新一代面向多核的编程语言如golang rust-lang等采用作为并发的基本单位。 协程可以被认为是一种用户空间线程,与传统的抢占式线程相比,有2个主要的优点: 与线程不同,协程是自己主动让出CPU,并交付他期望的下一个协程运行,而不是在任何时候都有可能被系统调度打断。因此协程的使用更加清晰易懂,并且多数情况下不需要锁机制。 与线程相比,协程的切换由程序控制,发生在用户空间而非内核空间,因此切换的代价非常的小。 green化 术语“green化”来自于python下著名的协程greenlet,指改造IO对象以能和协程配合。某种意义上,协程与线程的关系类似与线程与进程的关系,多个协程会在同一个线程的上下文之中运行。因此,当出现IO操作的时候,为了能够与协程相互配合,只阻塞当前协程而非整个线程,需要将io对象“green化”。目前orchid提供的green化的io对象包括: tcp socket(还不支持udp) descriptor(目前仅支持非文件类型文件描述符,如管道和标准输入/输出,文件类型的支持会在以后版本添加) timer (定时器) signal (信号) chan:协程间通信 chan这个概念引用自golang的chan。每个协程是一个独立的执行单元,为了能够方便协程之间的通信/同步,orchid提供了chan这种机制。chan本质上是一个阻塞消息队列,后面我们将看到,chan不仅可以用于同一个调度器上的协程之间的通信,而且可以用于不同调度器上的协程之间的通信。 多核 建议使用的scheduler per cpu的的模型来支持多核的机器,即为每个CPU核心分配一个调度器,有多少核心就创建多少个调度器。不同调度器的协程之间也可以通过chan来通信。协程应该被创建在哪个调度器里由用户自己决定。 进一步信息请阅读doc目录下tutorial。如果您发现任何bug或者有任何改进意见,请联系ioriiod0@gmail.com 标签:orchid
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

望闻问嵌

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值