📢:如果你也对机器人、人工智能感兴趣,看来我们志同道合✨
📢:不妨浏览一下我的博客主页【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
可能是一个类型别名,用于表示协程的执行状态,例如使用汇编语言或特定库函数对协程进行处理。
HeapSize
和pHeap
成员变量用于处理堆内存的分配和管理,可能在协程执行过程中需要动态分配一些内存。
协程初始化
#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_UNTIL
、PT_WAIT_WHILE
、PT_WAIT_THREAD
等等可能导致协程暂停的语句。
具体地说,当协程启动后,以下情况可能发生:
- 如果协程内部没有任何暂停语句(如
PT_WAIT_UNTIL
或PT_WAIT_WHILE
),那么协程会立即执行完内部代码块中的所有语句,然后结束。- 如果协程内存在暂停语句,协程的执行将会受到这些暂停语句的控制。当遇到一个暂停语句时,协程将暂停执行并返回到协程被调度的位置,等待满足暂停条件后再次恢复执行。
在整个协程执行过程中,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。