• zephyr-os 线程


    目录

    一、线程

    1. 相关概念

    2. 线程创建方式1[动态创建]

    2.1 创建线程示例

    3. 线程创建方式2[静态创建]

    3.1 创建线程示例

    3.2 关于线程优先级和延迟启动问题

    4. 结束一个线程

    4.1 线程的正常结束

    4.2 异常结束

    4.3 调用API结束

    5. 线程的选项字

    5.1 必须线程(essential thread)

    5.2 线程使用 CPU 的浮点寄存器和 SSE 寄存器

    6. 线程的调度问题

    6.1 线程休眠函数k_sleep()

    6.2 示例代码

    7. 线程的挂起和恢复

    7.1 线程挂起

    7.2 线程取消挂起(恢复执行)

    7.3 示例

    8. 总结

    8.1 线程挂起和结束的区别

    8.2 线程挂起和休眠的区别


    一、线程

    线程是轻量级的,不支持抢占的。一般用于设备驱动和其他比较重要的任务。线程的调度以优先级为参考,高优先级的线程会先得到执行。被调度的线程会持续执行,直到有阻塞操作才会停止。

    1. 相关概念

    (1)栈区大小:这是一段内存区域,是线程栈区。可以根据实际情况自定义栈区的大小,单位为字节。

    (2)线程入口函数:线程启动时调用的函数(执行的函数)。该函数最多可接收三个参数。

    (3)线程调度的优先级:决定内核调度器给该线程分配的CPU时间(系统在某个时刻只能执行一个线程,大多数系统都用的是时间片轮换算法,就是多个进程在分配到的极短时间片轮流使用CPU,可参考“调度”这节内容)。

    (4)线程选项:内核支持一系列 线程选项(thread options),允许线程在特殊情况下被特殊对待。

    (5)启动延时:在启动线程之前,设置延时启动时间,即允许线程延迟启动。

    2. 线程创建方式1[动态创建]

    调用线程创建函数k_thread_create()来创建一个线程,并决定是否立刻启动该线程。

    函数原型

    k_tid_t k_thread_create(struct k_thread *new_thread,

    k_thread_stack_t stack,size_t stack_size,

    void (*entry)(void *, void *, void*),void *p1, void *p2, void *p3,

    int prio, u32_t options, s32_t delay)

    函数功能

    创建一个线程。

    参数

    (1)struct k_thread *new_thread:线程控制块。是一个结构体指针,传入的是结构体的地址。

    (2)k_thread_stack_t stack指向线程的栈区的指针。跳转代码,可以发现,k_thread_stack_t是个指针数据类型,如下:

     注意到这句注释:

    Stacks should always be created with K_THREAD_STACK_DEFINE().

    即栈区的定义,需要使用到这个宏来定义,并且,要定义成全局的[main()之外,各种函数体之外]。

    跳到K_THREAD_STACK_DEFINE()的定义处:

     这是个带参数的宏,第一个参数:指向栈区的符号名称;第二个参数:栈区的大小。可以发现,实际上这块空间被定义成了一个数组,而数组名代表数组的首地址(第一个元素的)。

    (3)size_t stack_size:栈区的大小。

    (4)void (*entry)(void *, void *, void*):入口函数。函数名本身就是地址,所以定义好函数,直接传入函数名即可。

    (5)void *p1, void *p2, void *p3:在启动线程的时候,可以向入口函数传递三个参数。这里就很灵活,可以传任何数据类型的数据,定义对应即可。

    (6)int prio:该线程的优先级。

    (7)u32_t options:该线程的一些特殊选项。

    (8)s32_t delay:决定是否需要延时启动线程【单位:ms】,如果需要创建一个立即启动的线程,那么就填入K_NO_WAIT。实际上K_NO_WAIT被定义成0,也就是延时0ms,就是不延时。

    返回值

    线程的标识符(ID号)

    定义处(源文件)

    xxxxxx\kernel\thread.c

    声明处(头文件)

    xxxxxx\include\kernel.h

    2.1 创建线程示例

    main.c

    1. /*
    2. * Copyright (c) 2012-2014 Wind River Systems, Inc.
    3. *
    4. * SPDX-License-Identifier: Apache-2.0
    5. */
    6. #include
    7. #include
    8. #define MY_STACK_SIZE 500
    9. #define MY_PRIORITY 5
    10. #define main_sleep_time 2000
    11. #define pthread_sleep_time 3000
    12. //这行代码一定要定义成全局的,否则编译不通过
    13. K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE);
    14. // 线程入口函数
    15. void my_entry_point(void *str1, void *str2, void *str3) {
    16. // user application code
    17. printk("%s\r\n", "my_entry_point");
    18. while(1){
    19. k_sleep(pthread_sleep_time);
    20. printk("str1=%s str2=%s str3=%s\r\n",str1,str2,str3);
    21. }
    22. }
    23. void main(void)
    24. {
    25. printk("CONFIG_ARCH=%s\n", CONFIG_ARCH);
    26. struct k_thread my_thread_data;
    27. k_tid_t my_tid = k_thread_create(&my_thread_data, my_stack_area, /* 线程栈指针 */
    28. K_THREAD_STACK_SIZEOF(my_stack_area), /* 栈大小 */
    29. my_entry_point, /* 线程处理函数 */
    30. "123", "456", "789", /* 执行入口函数时传入的参数 */
    31. MY_PRIORITY, /* 线程优先级 */
    32. 0, /* 不使用选项字 */
    33. K_NO_WAIT); /* 立即启动 */
    34. while(1){
    35. k_sleep(main_sleep_time);
    36. printk("CONFIG_K_THREAD_SIZE=%d\r\n", CONFIG_K_THREAD_SIZE);
    37. }
    38. }

    Zephyr.h里面有包含kernel.h,所以可以不用单独#include

    3. 线程创建方式2[静态创建]

    可以直接使用宏K_THREAD_DEFINE()静态创建线程

    3.1 创建线程示例

    main.c

    1. /*
    2. * Copyright (c) 2012-2014 Wind River Systems, Inc.
    3. *
    4. * SPDX-License-Identifier: Apache-2.0
    5. */
    6. #include
    7. #include
    8. #define MY_STACK_SIZE 500
    9. #define MY_PRIORITY 5
    10. #define main_sleep_time 2000
    11. #define pthread_sleep_time 3000
    12. // 线程入口函数
    13. void my_entry_point(void *str1, void *str2, void *str3) {
    14. // user application code
    15. printk("%s\r\n", "my_entry_point");
    16. while(1){
    17. k_sleep(pthread_sleep_time);
    18. printk("str1=%s str2=%s str3=%s\r\n",str1,str2,str3);
    19. }
    20. }
    21. K_THREAD_DEFINE(my_tid, MY_STACK_SIZE,
    22. my_entry_point, "123", "456", "789",
    23. MY_PRIORITY, 0, K_NO_WAIT);
    24. void main(void)
    25. {
    26. printk("CONFIG_ARCH=%s\n", CONFIG_ARCH);
    27. while(1){
    28. k_sleep(main_sleep_time);
    29. printk("CONFIG_K_THREAD_SIZE=%d\r\n", CONFIG_K_THREAD_SIZE);
    30. }
    31. }

    3.2 关于线程优先级和延迟启动问题

    A.静态创建一个立即启动的线程,需要注意线程的优先级。

    如果线程优先级大于main()的优先级,那么线程可以先于mian()执行,如果低于main()的优先级,则后于mian()执行。之后就是调度的事了。

    例:上面的代码中定义线程的优先级是5  #define MY_PRIORITY 5,而主线程(main()线程)的优先级是6。主线程的优先级在工程配置文件prj.conf里面有定义,如下:

     对应编译出来的.config文件

     也就是说,静态创建的这个线程应该是先于main()执行的,运行串口打印信息如下:

     B.关于延迟启动问题

    虽然创建的线程优先级比较高,但是如果延时启动该线程,那么它还是会后于main()得到执行(这是调度的内容),所以并不是说,优先级高的线程就会先执行。

    例:延迟3秒启动线程

     这是串口打印的内容

     所以需要特别注意的一个问题,如果在线程做一些初始化操作,要注意有可能初始化没完成,其他线程就会去使用。

    4. 结束一个线程

    4.1 线程的正常结束

    可以在入口函数里面直接返回(return)跳出while(1),同步结束执行,这种方式称为正常结束。伪代码如下:

    1. void my_entry_point(int unused1, int unused2, int unused3) {
    2. while (1) {
    3. ...
    4. if () {
    5. return; /* thread terminates from mid-entry point function */
    6. }
    7. ...
    8. }
    9. /* thread terminates at end of entry point function */
    10. }

    示例代码:

    1. // 线程入口函数
    2. void my_entry_point(void *str1, void *str2, void *str3)
    3. {
    4. // user application code
    5. int Cnt=0;
    6. printk("%s\r\n", "my_entry_point");
    7. while(1){
    8. k_sleep(pthread_sleep_time);
    9. printk("str1=%s str2=%s str3=%s\r\n",str1,str2,str3);
    10. Cnt++;
    11. if(Cnt==3){
    12. Cnt=0;
    13. //break; //下面的语句能得到执行
    14. return; //函数直接返回,下面的语句得不到执行
    15. }
    16. }
    17. printk("my_entry_point---exit()\r\n");
    18. }

    在入口函数内部设置终止条件,满足条件则直接返回,正常结束线程,之后就只有主线程在运行。串口打印如下:

    4.2 异常结束

    如果线程触发了一个致命错误,内核将自动终止该线程。

    4.3 调用API结束

    线程自己或者其他线程调用k_thread_abort()函数来终止线程。

    函数原型

    void k_thread_abort(k_tid_t thread)

    {

    ……………………………………………………………….

    }

    函数功能

    中止(abord)一个线程的执行,后面的代码都得不到执行。跟直接return的效果是一样的。

    参数

    创建线程时,返回的线程ID,也就是指定要结束的线程的ID号。

    返回值

    定义处(源文件)

    xxxxxx\kernel\thread_abort.c

    声明处(头文件)

    xxxxxx\include\kernel.h

    示例代码:

    1. /*
    2. * Copyright (c) 2012-2014 Wind River Systems, Inc.
    3. *
    4. * SPDX-License-Identifier: Apache-2.0
    5. */
    6. #include
    7. #include
    8. #define MY_STACK_SIZE 500
    9. //主线程的优先级是6
    10. //这里设置线程的优先级为8[让main()先跑]
    11. #define MY_PRIORITY 8
    12. #define main_sleep_time 2000
    13. #define pthread_sleep_time 3000
    14. k_tid_t my_tid=NULL;
    15. //这行代码一定要定义成全局的,否则编译不通过
    16. K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE);
    17. // 线程入口函数
    18. void my_entry_point(void *str1, void *str2, void *str3)
    19. {
    20. // user application code
    21. int Cnt=0;
    22. printk("%s\r\n", "my_entry_point");
    23. while(1){
    24. k_sleep(pthread_sleep_time);
    25. printk("str1=%s str2=%s str3=%s\r\n",str1,str2,str3);
    26. Cnt++;
    27. if(Cnt==2){
    28. Cnt=0;
    29. //return;//正常结束
    30. //break;
    31. if(my_tid!=NULL)
    32. k_thread_abort(my_tid);//调用API结束
    33. }
    34. }
    35. printk("my_entry_point---exit()\r\n");
    36. }
    37. void main(void)
    38. {
    39. printk("CONFIG_ARCH=%s\n", CONFIG_ARCH);
    40. struct k_thread my_thread_data;
    41. my_tid = k_thread_create(&my_thread_data, my_stack_area,/* 线程栈指针 */
    42. K_THREAD_STACK_SIZEOF(my_stack_area), /* 栈大小 */
    43. my_entry_point, /* 线程处理函数和传入参 */
    44. "123", "456", "789",
    45. MY_PRIORITY, /* 线程优先级 */
    46. 0, /* 不使用选项字 */
    47. K_NO_WAIT); /* 立即启动 */
    48. if(my_tid==NULL){
    49. printk("fail to create thread\r\n");
    50. }else{
    51. printk("success to create thread\r\n");
    52. }
    53. while(1){
    54. k_sleep(main_sleep_time);
    55. printk("CONFIG_K_THREAD_SIZE=%d\r\n", CONFIG_K_THREAD_SIZE);
    56. }
    57. }

    串口打印:

    5. 线程的选项字

    内核支持一系列线程选项(thread options),以允许线程在特殊情况下被特殊对待。这些与线程关联的选项在线程创建时就被指定了。

    如果不使用选项字,则该参数填零。如果线程需要选项,可以通过选项名指定。如果需要多个选项,使用符号 | 作为分隔符。(即按位或操作符)。

    这些选项字都以宏定义的形式定义在kernel.h中:

    5.1 必须线程(essential thread)

    选项字为:K_ESSENTIAL。表明线程是不可以被中止的,所以不管线程是正常结束或者是异常中止,内核都认为是产生了一个致命的系统错误。

    示例代码:

    1. /*
    2. * Copyright (c) 2012-2014 Wind River Systems, Inc.
    3. *
    4. * SPDX-License-Identifier: Apache-2.0
    5. */
    6. #include
    7. #include
    8. #define MY_STACK_SIZE 500
    9. //主线程的优先级是6
    10. //这里设置线程的优先级为8[让main()先跑]
    11. #define MY_PRIORITY 8
    12. #define main_sleep_time 2000
    13. #define pthread_sleep_time 3000
    14. k_tid_t my_tid=NULL;
    15. //这行代码一定要定义成全局的,否则编译不通过
    16. K_THREAD_STACK_DEFINE(my_stack_area, MY_STACK_SIZE);
    17. // 线程入口函数
    18. void my_entry_point(void *str1, void *str2, void *str3)
    19. {
    20. // user application code
    21. int Cnt=0;
    22. printk("%s\r\n", "my_entry_point");
    23. int *pt=0;
    24. while(1){
    25. k_sleep(pthread_sleep_time);
    26. printk("str1=%s str2=%s str3=%s\r\n",str1,str2,str3);
    27. Cnt++;
    28. if (Cnt==2) {
    29. Cnt=0;
    30. //return;//正常结束
    31. //break;
    32. printf("pt=%d",*pt+2); //异常结束
    33. #if 0
    34. if(my_tid!=NULL)
    35. k_thread_abort(my_tid);//调用API结束
    36. #endif
    37. }
    38. }
    39. printk("my_entry_point---exit()\r\n");
    40. }
    41. void main(void)
    42. {
    43. printk("CONFIG_ARCH=%s\n", CONFIG_ARCH);
    44. struct k_thread my_thread_data;
    45. my_tid = k_thread_create(&my_thread_data, my_stack_area,/* 线程栈指针*/
    46. K_THREAD_STACK_SIZEOF(my_stack_area),/* 栈大小*/
    47. my_entry_point, /* 线程处理函数和传入参数*/
    48. "123", "456", "789",
    49. MY_PRIORITY, /* 线程优先级 */
    50. K_ESSENTIAL, /* 不可中止的线程 */
    51. K_NO_WAIT); /* 立即启动 */
    52. if (my_tid==NULL) {
    53. printk("fail to create thread\r\n");
    54. } else {
    55. printk("success to create thread\r\n");
    56. }
    57. while(1){
    58. k_sleep(main_sleep_time);
    59. printk("CONFIG_K_THREAD_SIZE=%d\r\n", CONFIG_K_THREAD_SIZE);
    60. }
    61. }

    按照官方文档的说法,在不可中止的线程里面操作空指针,是会导致系统奔溃的。一般来说,空指针的操作会导致崩溃。比如X86平台的VS:

     但是应用实际中可能还跟实现相关,空指针跟CPU架构、芯片的设计(0地址是否有效,是否已经映射使得0地址合法)。

    注意:一般情况下,普通创建的线程都不是必须线程。

    5.2 线程使用 CPU 的浮点寄存器和 SSE 寄存器

    指定线程使用CPU的浮点寄存器:K_FP_REGS

    指定线程使用CPU的SSE寄存器:K_SSE_REGS

    这两个选项都是跟X86架构相关的选项,可以不用理会。

    6. 线程的调度问题

    详细的调度相关理论放到另一个文档,目前只需要知道线程是如何依靠优先级进行调度的。内核调度线程的基本依据:(1)优先级  (2)线程休眠(让出CPU使用权)

    6.1 线程休眠函数k_sleep()

    函数原型

    void k_sleep(s32_t duration){

    ……………………………………………………………….

    }

    函数功能

    休眠当前线程,让出CPU使用权,后面按照优先级进行排队的线程才会得以执行。如果不休眠,则会一直卡在当前线程,其他线程得不到调度。------->线程调度

    参数

    休眠时间。

    返回值

    定义处(源文件)

    xxxxxx\kernel\sched.c

    声明处(头文件)

    xxxxxx\include\kernel.h

    6.2 示例代码

    示例1

    主线程优先级:6     自定义线程1优先级:7    自定义线程2优先级:8

    main.c

    1. /*
    2. * Copyright (c) 2012-2014 Wind River Systems, Inc.
    3. *
    4. * SPDX-License-Identifier: Apache-2.0
    5. */
    6. #include
    7. #include
    8. #define MY_STACK_SIZE 500
    9. #define MY_PRIORITY1 7
    10. #define MY_PRIORITY2 8
    11. void my_entry_point1(void *pt1,void *pt2,void *pt3)
    12. {
    13. printk("%s\r\n", "my_entry_point1");
    14. while(1){
    15. k_sleep(1000);
    16. printk("pthread1_run\r\n");
    17. }
    18. }
    19. void my_entry_point2(void *pt1,void *pt2,void *pt3)
    20. {
    21. printk("%s\r\n", "my_entry_point2");
    22. while(1){
    23. k_sleep(1000);
    24. printk("pthread2_run\r\n");
    25. }
    26. }
    27. K_THREAD_DEFINE(my_tid1, MY_STACK_SIZE,my_entry_point1, NULL, NULL, NULL,MY_PRIORITY1, 0, 0);
    28. K_THREAD_DEFINE(my_tid2, MY_STACK_SIZE,my_entry_point2, NULL, NULL, NULL,MY_PRIORITY2, 0, 0);
    29. void main(void)
    30. {
    31. while(1){
    32. k_sleep(1000);
    33. printf("main_run\r\n");
    34. }
    35. }

    调度的顺序应该是按照优先级从高到低,串口打印如下:

     示例2:主线程不休眠,不让出CPU使用权。

    void main(void)

    {

             while(1){

                       //k_sleep(1000);

                       //printf("main_run\r\n");

             }

    }

    后面的两个线程根本得不到调度,串口打印如下:

     示例3:线程1不休眠,不让出CPU使用权

    1. #include
    2. #include
    3. #define MY_STACK_SIZE 500
    4. #define MY_PRIORITY1 7
    5. #define MY_PRIORITY2 8
    6. void my_entry_point1(void *pt1,void *pt2,void *pt3)
    7. {
    8. printk("%s\r\n", "my_entry_point1");
    9. while(1){
    10. //k_sleep(1000);
    11. printk("pthread1_run\r\n");
    12. }
    13. }
    14. void my_entry_point2(void *pt1,void *pt2,void *pt3)
    15. {
    16. printk("%s\r\n", "my_entry_point2");
    17. while(1){
    18. k_sleep(1000);
    19. printk("pthread2_run\r\n");
    20. }
    21. }
    22. K_THREAD_DEFINE(my_tid1, MY_STACK_SIZE,my_entry_point1, NULL, NULL, NULL,MY_PRIORITY1, 0, 0);
    23. K_THREAD_DEFINE(my_tid2, MY_STACK_SIZE,my_entry_point2, NULL, NULL, NULL,MY_PRIORITY2, 0, 0);
    24. void main(void)
    25. {
    26. while(1){
    27. k_sleep(1000);
    28. //printf("main_run\r\n");
    29. }
    30. }

    主线程是最先启动的,然后让出CPU使用权,自定义线程1优先级是7,所以轮到线程1执行,到它执行的时候,就不让出CPU使用权,CPU就一直执行线程1了(不会在三个线程中正常调度,轮流执行)。串口打印:

     同样的,如果调度到线程2,没有k_sleep(),不让出CPU使用权,也会是同样的效果。跟main()线程不让出CPU使用权一样的道理。

    所以,写应用,写多线程的时候,要注意两个问题:(1)线程的优先级[决定线程占有CPU的先后顺序] (2)k_sleep(),是否让出CPU使用权。

    7. 线程的挂起和恢复

    7.1 线程挂起

    函数原型

    void k_thread_suspend(struct k_thread *thread){

    ……………………………………………………………………………

    }

    函数功能

    线程被挂起,则线程就会停止执行。可以挂起包括调用线程在内的所有线程(在线程内部调用函数将自己挂起或者将别的线程挂起),对已经挂起的线程再次挂起时不会产生任何效果。

    线程一旦被挂起,它将一直不能被调度,除非另一个线程调用 k_thread_resume() 取消挂起(恢复执行)指定的线程。

    参数

    线程ID

    返回值

    定义处(源文件)

    ATS350B\kernel\thread.c

    声明处(头文件)

    ATS350B\include\kernel.h

    7.2 线程取消挂起(恢复执行)

    函数原型

    void k_thread_resume(struct k_thread *thread){

    …………………………………………………………………………..

    }

    函数功能

    取消挂起(恢复执行)指定的线程。

    参数

    线程ID

    返回值

    定义处(源文件)

    ATS350B\kernel\thread.c

    声明处(头文件)

    ATS350B\include\kernel.h

    7.3 示例

    通过判断获取到的shell命令行的参数来决定来挂起或者取消挂起指定的线程。

    1. #include
    2. #include
    3. #include /*Shell*/
    4. #define MY_STACK_SIZE 500
    5. #define MY_PRIORITY1 7
    6. #define MY_PRIORITY2 8
    7. void my_entry_point1(void *pt1,void *pt2,void *pt3)
    8. {
    9. printk("%s\r\n", "my_entry_point1");
    10. while(1){
    11. k_sleep(2000);
    12. printk("pthread1_run\r\n");
    13. }
    14. }
    15. void my_entry_point2(void *pt1,void *pt2,void *pt3)
    16. {
    17. printk("%s\r\n", "my_entry_point2");
    18. while(1){
    19. k_sleep(2000);
    20. printk("pthread2_run\r\n");
    21. }
    22. }
    23. K_THREAD_DEFINE(my_tid1, MY_STACK_SIZE,my_entry_point1, NULL, NULL, NULL,MY_PRIORITY1, 0, 0);
    24. K_THREAD_DEFINE(my_tid2, MY_STACK_SIZE,my_entry_point2, NULL, NULL, NULL,MY_PRIORITY2, 0, 0);
    25. /*Shell*/
    26. static int get_shell_dat(int argc, char *argv[])
    27. {
    28. #if 0
    29. for (int i=0; i < argc; i++)
    30. printk("Argument %d is %s\r\n", i, argv[i]);
    31. #endif
    32. #if 0
    33. //这地方不会相等
    34. if (argv[1] == "suspend1") {
    35. k_thread_suspend(my_tid1);
    36. }
    37. #endif
    38. //只能判断是否包含
    39. if (strstr(argv[1],"suspend1")) {
    40. k_thread_suspend(my_tid1);
    41. } else if (strstr(argv[1],"resume1")) {
    42. k_thread_resume(my_tid1);
    43. } else if(strstr(argv[1],"suspend2")) {
    44. k_thread_suspend(my_tid2);
    45. } else if(strstr(argv[1],"resume2")) {
    46. k_thread_resume(my_tid2);
    47. } else{
    48. ;
    49. }
    50. }
    51. /*Shell*/
    52. static const struct shell_cmd consumer_commands[] = {
    53. { "1", get_shell_dat, "consumer" }, /*前缀*/
    54. };
    55. int main(void)
    56. {
    57. /*Shell*/
    58. SHELL_REGISTER("1", consumer_commands); /*前缀*/
    59. while(1){
    60. printf("main_run\r\n");
    61. k_sleep(2000);
    62. }
    63. return 0;
    64. }

    注:

    1) 跟获取Shell命令行参数相关的几个地方,看注释/*Shell*/

    2) 在Shell中输入参数后,按下回车键,shell子系统才会获取到参数

    所以,参数中可能多了回车或者换行符,因此不能直接进行判断,具体看代码里面的注释,关键地方如下:

    …………………………………………………………………………………..

    #if 0

        //这地方不会相等

             if(argv[1] == "suspend1"){

    k_thread_suspend(my_tid1);

    }

    #endif

        //只能判断是否包含

             if(strstr(argv[1],"suspend1")){

                       k_thread_suspend(my_tid1);

             }

    …………………………………………………………………………………..

    3) shell命令行输入的命令

    1 1 suspend1  //前面两个是前缀,可自由定义,具体对应代码里面的注释/*前缀*/

    1 1 resume1

    1 1 suspend2

    1 1 resume2

    4) 串口打印

    ***** BOOTING ZEPHYR OS v1.9.0 - BUILD: Nov  6 2019 15:00:54 *****

    main_run

    my_entry_point1

    my_entry_point2

    main_run

    pthread1_run

    pthread2_run       //主线程和两个自定义线程都正常运行和调度

    shell> 1 1 suspend1  //从Shell中输入挂起线程1指令

    shell>

    main_run          //线程1已被挂起(暂停执行),只有主线程和线程2在跑

    pthread2_run

    main_run

    pthread2_run

    shell> 1 1 suspend2  //从Shell中输入挂起线程2指令

    shell>

    main_run          //线程2也被挂起(暂停执行)了,只剩下主线程在跑

    main_run

    main_run

    main_run

    main_run

    main_run

    main_run

    shell> 1 1 resume1  //从Shell中输入取消挂起线程1指令

    shell>

    pthread1_run      //线程1继续执行

    main_run

    pthread1_run

    main_run

    shell> 1 1 resume2  //从Shell中输入线程2恢复执行指令

    shell>

    pthread2_run      //线程2继续执行

    main_run         //主线程和两个自定义线程都正常运行和调度

    pthread1_run

    pthread2_run

    main_run

    pthread1_run

    pthread2_run

    8. 总结

    8.1 线程挂起和结束的区别

    (1) 线程的挂起和恢复,仅仅是线程的暂停执行和继续执行,并不是完全退出,可以看到,线程恢复执行的时候,并没有执行这行代码printk("%s\r\n", "my_entry_point1");

    (2) 而结束一个线程之后,只能再次重新创建。

    8.2 线程挂起和休眠的区别

    线程可以使用 k_sleep() 睡眠一段指定的时间。不过,这与挂起不同,睡眠线程在睡眠时间完成后会自动运行,而挂起的话,再次运行需要调用k_thread_resume()。

  • 相关阅读:
    Android逆向学习之Frida逆向与抓包实战学习笔记(持续更新中)
    Hadoop HA搭建
    【Python笔记-设计模式】原型模式
    C++ 窗体程序初步(全网最全)
    ubuntu添加环境变量
    计算机专业的学生需要每天刷题吗?
    《痞子衡嵌入式半月刊》 第 60 期
    基础算法--理解递归
    CSS 实现音频loding动画
    基于中国新能源汽车税收政策下成都市场发展路线研究
  • 原文地址:https://blog.csdn.net/qq_40088639/article/details/127615660