基于C语言构建一个独立栈协程和共享栈协程的任务调度系统

 更新时间:2024年02月05日 15:42:53   作者:大橙子疯  
这篇文章主要为大家详细介绍了如何基于C语言构建一个独立栈协程和共享栈协程的任务调度系统,文中的示例代码讲解详细,需要的可以参考下

脚本之家 / 编程助手:解决程序员“几乎”所有问题!
脚本之家官方知识库 → 点击立即使用

使用了标准库头文件 <setjmp.h>中的 setjmp 和 longjmp两个函数,构建了一个简单的查询式协作多任务系统,支持独立栈共享栈两种任务。

其中涉及到获取和设置栈的地址操作,因此还需要根据不同平台提供获取和设置栈的地址操作(一般是汇编语言,因为涉及到寄存器)

该调度系统仅运行在一个实际的线程中,因此本质上属于协程

独立栈任务都有自己独立的运行栈空间,互不干扰;共享栈任务共用一个运行栈空间。

特点

无任务优先级抢占的功能。

任务切换的时机完全取决于正在运行的任务,体现协作

支持独立栈共享栈两种任务,根据不同的应用场景决定。

查询式的调度方式,当前任务切换时,查询下个任务是否需要执行。

移植性强,只需要修改设置栈和获取当前栈地址的宏即可。

相对于时间片论法的任务调度来说,查询式协作多任务系统有以下特点:

  • 无需使用定时器做为任务调度
  • 每个任务都可以使用while循环,用于执行任务并保持程序的运行,程序结构清晰
  • 每个任务都可以随时阻塞等待,甚至可以在嵌套的子函数中阻塞等待
  • 通过阻塞等待,无需使用状态机等较为复杂的方式来优化缩减每个任务的执行时长

相对于RTOS操作系统来说,查询式协作多任务系统有以下特点:

  • 没有任务优先级抢占式的功能,因此临界资源(中断除外)和优先级反转的问题也不存在
  • 允许用户或应用程序根据需要自由地切换到下一个就绪任务
  • 通过自主调度和管理任务,查询式协作多任务系统可以提高工作效率
  • 没有操作系统的复杂

功能设计

运行栈空间:程序运行中发生函数调用等情况需要使用的栈内存空间

独立栈任务(有栈任务)

每个独立栈任务都拥有自己独立的运行栈空间,可以随时随地阻塞等待,保存上下文后切换到下一个任务执行

独立栈任务在切换下一个任务时,不会操作运行栈,只对上下文切换

共享栈任务(无栈任务)

每个共享栈任务都没有自己独立的运行栈空间,虽然也能阻塞等待,但是仅限于在任务入口函数中使用,禁止在任务的子函数(嵌套函数)中阻塞等待;并且在该任务入口函数中不建议定义相关变量。

  • 每个任务有自己的独立备份栈(用来备份运行栈的栈顶部分数据);运行栈通常比备份栈要大很多,否则任务函数无法正常运行多级嵌套的函数
  • 共享栈任务在切换下一个任务时会将当前运行栈(共享栈)提前设置好的备份栈大小(宏配置)拷贝到内存备份起来,等下次即将执行时再从内存中拷贝到运行栈(共享栈)进行恢复
  • 通过修改加大备份栈大小(宏配置)的值,可以在共享栈任务入口函数定义变量,这样可以避免这些变量的值没有备份导致丢失,或者通过 static 定义局部变量
  • 该类型任务适合于轻量的任务处理,一般都是调用封装好的函数即可

注:这里的共享栈任务和常规的实现有一些差异,常规的实现是使用堆申请内存保存栈的数据,用多少申请多少进行保存,而这里的实现仅仅保存了一部分数据。

任务创建

在调度系统启动前,至少要先创建一个任务,否则直接退出

可以在任务中创建新的任务,不管是独立栈任务还是共享栈任务

  • 独立栈任务中可以创建新的独立栈任务和共享栈任务
  • 共享栈任务中同样可以创建新的独立栈任务和共享栈任务,而且在创建共享栈任务时可以使用同一个共享栈

独立栈任务和共享栈任务一共可以创建最多32个任务(需要修改宏配置)

任务销毁

  • 没有提供该功能接口函数,任务入口函数主动退出则自动将任务销毁。
  • 可以通过等待任务退出接口函数在其他任务中等待该任务退出。

任务阻塞

当前任务阻塞提供两种方式:

  • 时间阻塞:需要阻塞多长时间,等时间满足后才会继续执行
  • 事件阻塞:通过事件阻塞,只有事件触发后才会继续执行

使用说明

任务创建/退出

对于创建独立栈任务还是共享栈任务的示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
uint8_t g_task1Stack[1024 * 2];
uint8_t g_task2Stack[1024 * 2];
uint8_t g_task3Stack[1024 * 2];
 
uint8_t g_sharedStack[1024 * 2];
 
// 执行完成就退出的任务
void taskfunc3(int arg)
{
    ...
    cotOs_Wait(1000);
    ...
    cotOs_Wait(1000);
}
 
void taskfunc1(int arg)
{
   /* 不管taskfunc1是独立栈任务还是共享栈任务,都支持创建子任务 */
   cotOs_CreatTask(taskfunc3, COT_OS_UNIQUE_STACK, g_task3Stack, sizeof(g_task3Stack), 0);  // 创建独立栈任务
   cotOs_CreatTask(taskfunc3, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0); // 创建共享栈任务
 
    while (1)
    {
        ...
        cotOs_Wait(1000);
    }
}
 
void taskfunc2(int arg)
{
    while (1)
    {
        ...
        cotOs_Wait(10);
    }
}
 
int main(void)
{
    cotOs_Init(GetTimerMs);
#if 0
    /* 创建独立栈任务 */
    cotOs_CreatTask(taskfunc1, COT_OS_UNIQUE_STACK, g_task1Stack, sizeof(g_task1Stack), 0);
    cotOs_CreatTask(taskfunc2, COT_OS_UNIQUE_STACK, g_task2Stack, sizeof(g_task2Stack), 0);
#else
    /* 创建共享栈任务 */
    cotOs_CreatTask(taskfunc1, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);
    cotOs_CreatTask(taskfunc2, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);
#endif
    cotOs_Start();
}

任务限制

对于创建独立栈任务还是共享栈任务,共享栈任务有限制要求,禁止在任务入口函数的嵌套函数中阻塞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
uint8_t g_task1Stack[1024 * 2];
uint8_t g_sharedStack[1024 * 2];
 
void func1_1(void)
{
    ...
    cotOs_Wait(1000);
    ...
    cotOs_Wait(1000);
}
 
/* 独立栈任务 */
void taskfunc1(int arg)
{
    int arr[10];   // 可以直接定义变量使用
 
    while (1)
    {
        func1_1();  // 可以在嵌套函数中使用阻塞等待
        ...
        cotOs_Wait(1000);
    }
}
 
void func2_1(void)
{
    ...
}
 
/* 共享栈任务 */
void taskfunc2(int arg)
{
    static int arr[10];  // 建议使用static定义任务内变量或者不定义变量
 
    while (1)
    {
        func2_1();  // 禁止在嵌套函数中使用阻塞等待
        ...
        cotOs_Wait(10);
    }
}
 
int main(void)
{
    cotOs_Init(GetTimerMs);
 
    /* 创建独立栈任务 */
    cotOs_CreatTask(taskfunc1, COT_OS_UNIQUE_STACK, g_task1Stack, sizeof(g_task1Stack), 0);
 
    /* 创建共享栈任务 */
    cotOs_CreatTask(taskfunc2, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);
 
    cotOs_Start();
}

任务阻塞/退出

通过时间和事件的方式阻塞

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
uint8_t g_task1Stack[1024 * 2];
uint8_t g_task2Stack[1024 * 2];
uint8_t g_task3Stack[1024 * 2];
 
uint8_t g_sharedStack[1024 * 2];
 
CotOSCondition_t g_eventCv;
 
// 执行完成就退出的任务
void taskfunc3(int arg)
{
    ...
    cotOs_ConditionWait(&g_eventCv);
    ...
}
 
void taskfunc1(int arg)
{
   cotOsTask_t task = cotOs_CreatTask(taskfunc3, COT_OS_UNIQUE_STACK, g_task3Stack, sizeof(g_task3Stack), 0);
 
    while (1)
    {
        ...
        cotOs_Wait(1000);
 
        if (...)
        {
            // 等待 taskfunc3 任务运行结束后才退出 taskfunc1
            cotOs_Join(task);
            break;
        }
    }
}
 
void taskfunc2(int arg)
{
    while (1)
    {
        ...
        cotOs_Wait(10);
 
        if (...)
        {
            cotOs_ConditionNotify(&g_eventCv);  // 通知 taskfunc3 继续执行
        }
    }
}
 
int main(void)
{
    cotOs_Init(GetTimerMs);
    cotOs_CreatTask(taskfunc1, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);
    cotOs_CreatTask(taskfunc2, COT_OS_SHARED_STACK, g_sharedStack, sizeof(g_sharedStack), 0);
 
    cotOs_Start();
}

不同栈类型任务应用场景

独立栈任务(有栈任务)

  • 重量级任务: 提供更多的控制,适用于需要更精确地管理任务状态的情况和执行计算密集型任务的场景
  • 更可预测的内存使用: 在创建时分配栈空间,可以更好地控制内存使用,适用于需要更可预测内存行为的场景
  • 递归调用: 更容易处理递归调用,因为每个任务都有独立的栈空间

共享栈任务(无栈任务)

  • 轻量级任务: 通常更轻量,适用于大量小任务的场景。
  • 内存效率: 适用于内存受限的环境,因为不需要为每个任务分配各自的栈空间(备份栈除外)。

代码链接

cot_os

以上就是基于C语言构建一个独立栈协程和共享栈协程的任务调度系统的详细内容,更多关于C语言任务调度系统的资料请关注脚本之家其它相关文章!

蓄力AI

微信公众号搜索 “ 脚本之家 ” ,选择关注

程序猿的那些事、送书等活动等着你

原文链接:https://www.cnblogs.com/const-zpc/p/18007643

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若内容造成侵权/违法违规/事实不符,请将相关资料发送至 reterry123@163.com 进行投诉反馈,一经查实,立即处理!

相关文章

  • C++使用TinyXML2实现解析和生成XML数据

    C++使用TinyXML2实现解析和生成XML数据

    TinyXML2是一个轻量级的、开源的C++库,专门用于解析和生成XML文档,本文主要为大家介绍了如何使用TinyXML2实现解析和生成XML数据,需要的可以参考下
    2024-04-04
  • QT网络编程UDP下C/S架构广播通信(实例讲解)

    QT网络编程UDP下C/S架构广播通信(实例讲解)

    下面小编就为大家带来一篇QT网络编程UDP下C/S架构广播通信(实例讲解)。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-07-07
  • 详解C++构造函数

    详解C++构造函数

    这篇文章主要为大家介绍了C++构造函数,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2021-11-11
  • C++ LeetCode1805字符串不同整数数目

    C++ LeetCode1805字符串不同整数数目

    这篇文章主要为大家介绍了C++ LeetCode1805字符串不同整数数目,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-12-12
  • C++堆排序算法的实现方法

    C++堆排序算法的实现方法

    这篇文章主要介绍了C++堆排序算法的实现方法,很经典的算法,需要的朋友可以参考下
    2014-08-08
  • 简单讲解哈希表

    简单讲解哈希表

    本文主要介绍了哈希表简单知识及C语言实现哈希表实例,文中利用图片以及代码简单讲解了相关知识,感兴趣的小伙伴可以多多学习这篇文章
    2021-09-09
  • c/c++语言位域注意事项分析

    c/c++语言位域注意事项分析

    所谓“位域”是把一个字节中的二进位划分为几个不同的区域, 并说明每个区域的位数。每个域有一个域名,答应在程序中按域名进行操作
    2013-09-09
  • cocos2d-x学习笔记之CCLayer、CCLayerColor、CCLayerGradient、CCLayerMultiplex场景层介绍

    cocos2d-x学习笔记之CCLayer、CCLayerColor、CCLayerGradient、CCLayerMu

    这篇文章主要介绍了cocos2d-x学习笔记之CCLayer、CCLayerColor、CCLayerGradient、CCLayerMultiplex场景层介绍,需要的朋友可以参考下
    2014-09-09
  • C++的std::transform()的实现

    C++的std::transform()的实现

    在 C++ 标准库中,std::transform() 是一个非常有用的算法函数,它能够将给定范围中的每个元素进行变换,并将变换后的结果存储到另一个范围中,本文就详细的介绍一下具体用法,感兴趣的可以了解一下
    2023-08-08
  • C语言 二叉树的链式存储实例

    C语言 二叉树的链式存储实例

    本篇文章主要介绍C语言中二叉树的链式存储,这里提供了一个实例代码进行参考,这样对二叉树的链式存储有更深入的了解,希望能帮到学习这块知识的同学
    2016-07-07

最新评论