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

📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨
📢:不妨浏览一下我的博客主页【https://blog.csdn.net/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。

在这里插入图片描述

  • 8
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

嵌小超

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

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

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

打赏作者

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

抵扣说明:

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

余额充值