1、信号量

信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的。
每个信号量对象都有一个信号量值和一个线程等待队列,信号量的值对应了信号量对象的实例数目、资源数目,假如信号量值为 5,则表示共有 5 个信号量实例(资源)可以被使用,当信号量实例数目为零时,再申请该信号量的线程就会被挂起在该信号量的等待队列上,等待可用的信号量实例(资源)

1.1创建和删除信号量

  1. #define DBG_TAG "main"
  2. #define DBG_LVL DBG_LOG
  3. #include <rtdbg.h>
  4. rt_sem_t semout1 = NULL;
  5. int main(void)
  6. {
  7. //名字小于八个字符
  8. semout1 = rt_sem_create("sem1", 1, RT_IPC_FLAG_FIFO);
  9. if(semout1 == NULL)
  10. {
  11. LOG_E("semout1 is error.......\n");
  12. return -ENOMEM;
  13. }
  14. else {
  15. LOG_E("semout1 is successful.......\n");
  16. }
  17. //rt_sem_delete(semout1);
  18. return 0;
  19. }

1.2初始化和脱离信号量

  1. #define DBG_TAG "main"
  2. #define DBG_LVL DBG_LOG
  3. #include <rtdbg.h>
  4. struct rt_semaphore sem2;
  5. int main(void)
  6. {
  7. int ret2 = 0;
  8. ret2 = rt_sem_init(&sem2, "sem2", 2, RT_IPC_FLAG_FIFO);//如果直接定义指针就只有一个指针的大小;
  9. if(ret2 < 0)
  10. {
  11. LOG_E("sem2 is error.......\n");
  12. return ret2;
  13. }
  14. else {
  15. LOG_E("sem2 is successful.......\n");
  16. }
  17. //rt_sem_detach(&sem2);
  18. return 0;
  19. }

1.3获取释放信号量

线程通过获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号量,并且相应的信号量值会减 1,如果信号量的值等于零,那么说明当前信号量资源实例不可用,申请该信号量的线程将根据time参数的情况选择直接返回、或挂起等待一段时间、或永久等待,直到其他线程或中断释放该信号量。如果在参数time指定的时间内依然得不到信号量,线程将超时返回,返回值是 - RT_ETIMEOUT。

  1. #include <rtthread.h>
  2. #define DBG_TAG "main"
  3. #define DBG_LVL DBG_LOG
  4. #include <rtdbg.h>
  5. rt_sem_t semout1 = NULL;
  6. struct rt_semaphore sem2;
  7. int flags = 0;
  8. rt_thread_t rtt_ntr1 = NULL;
  9. rt_thread_t rtt_ntr2 = NULL;
  10. void th_entry1(void *parameter)
  11. {
  12. while(1)
  13. {
  14. rt_thread_mdelay(800);
  15. rt_sem_take(semout1, RT_WAITING_FOREVER);
  16. flags++;
  17. if(flags == 100)
  18. {
  19. flags = 0;
  20. }
  21. rt_kprintf("th_entry1 is running and the flags = %d.......\n\r",flags);
  22. rt_sem_release(&sem2);
  23. }
  24. }
  25. void th_entry2(void *parameter)
  26. {
  27. while(1)
  28. {
  29. rt_sem_take(&sem2, RT_WAITING_FOREVER);
  30. flags--;
  31. rt_kprintf("th_entry2 is running and the flags = %d.......\n\r",flags);
  32. rt_sem_release(semout1);
  33. rt_thread_mdelay(100);
  34. }
  35. }
  36. int main(void)
  37. {
  38. //名字小于八个字符
  39. semout1 = rt_sem_create("sem1", 1, RT_IPC_FLAG_FIFO);
  40. if(semout1 == NULL)
  41. {
  42. LOG_E("semout1 is error.......\n");
  43. return -ENOMEM;
  44. }
  45. else {
  46. LOG_E("semout1 is successful.......\n");
  47. }
  48. //rt_sem_delete(semout1);
  49. int ret2 = 0;
  50. ret2 = rt_sem_init(&sem2, "sem2", 0, RT_IPC_FLAG_FIFO);//如果直接定义指针就只有一个指针的大小;
  51. if(ret2 < 0)
  52. {
  53. LOG_E("sem2 is error.......\n");
  54. return ret2;
  55. }
  56. else {
  57. LOG_E("sem2 is successful.......\n");
  58. }
  59. //rt_sem_detach(&sem2);
  60. rtt_ntr1 = rt_thread_create("test",th_entry1,NULL,512,20,5);
  61. if(rtt_ntr1 == RT_NULL)
  62. {
  63. LOG_E("rt_thread_create1 error .....\n");
  64. return -RT_ENOMEM;
  65. }
  66. else {
  67. LOG_E("rt_thread_create1 successful .....\n");
  68. rt_thread_startup(rtt_ntr1);
  69. }
  70. rtt_ntr2 = rt_thread_create("test",th_entry2,NULL,512,20,5);
  71. if(rtt_ntr2 == RT_NULL)
  72. {
  73. LOG_E("rt_thread_create2 error .....\n");
  74. return -RT_ENOMEM;
  75. }
  76. else {
  77. LOG_E("rt_thread_create2 successful .....\n");
  78. rt_thread_startup(rtt_ntr2);
  79. }
  80. return 0;
  81. }

image.png

2、互斥量

互斥量体现的是排他性,也是解决多线程同时操作临界区临界资源导致的竟态的一种方法。(类似于特殊的信号量——二值信号量)
区别:信号量可由不同线程释放,互斥量只能由同一线程进行释放。

  1. rt_mutex_t mux1 = NULL;
  2. mux1 = rt_mutex_create("mutex1", RT_IPC_FLAG_FIFO);
  3. if(mux1 == NULL)
  4. {
  5. LOG_E("mutex1 is error.......\n");
  6. return -ENOMEM;
  7. }
  8. else {
  9. LOG_E("mutex1 is successful.......\n");
  10. }
  11. //rt_mutex_delete(mux1);

示例:

  1. #include <rtthread.h>
  2. #define DBG_TAG "main"
  3. #define DBG_LVL DBG_LOG
  4. #include <rtdbg.h>
  5. rt_mutex_t mutex1 ;
  6. struct rt_mutex mutex2;
  7. rt_thread_t th1 , th2;
  8. int flag1 = 0, flag2 = 0;
  9. void th1_entry(void *parameter)
  10. {
  11. while(1){
  12. rt_mutex_take(mutex1, RT_WAITING_FOREVER);
  13. flag1++;
  14. rt_thread_mdelay(1000);
  15. flag2++;
  16. rt_kprintf("th1_entry flag1:%d,flag2:%d\n",flag1,flag2);
  17. rt_mutex_release(mutex1);
  18. }
  19. }
  20. void th2_entry(void *parameter)
  21. {
  22. while(1){
  23. rt_mutex_take(mutex1, RT_WAITING_FOREVER);
  24. flag1++;
  25. flag2++;
  26. rt_mutex_release(mutex1);
  27. rt_kprintf("th2_entry flag1:%d,flag2:%d\n",flag1,flag2);
  28. rt_thread_mdelay(1000);
  29. }
  30. }
  31. int main(void)
  32. {
  33. //动态创建互斥锁
  34. mutex1 = rt_mutex_create("mutex1", RT_IPC_FLAG_FIFO);
  35. if(mutex1 == RT_NULL){
  36. LOG_E("mutex1 rt_mutex_create failed...\n");
  37. return -ENOMEM;
  38. }
  39. LOG_D("rt_mutex_create successed...\n");
  40. th1 = rt_thread_create("th1", th1_entry,RT_NULL,512, 20, 5);
  41. if(th1 == RT_NULL){
  42. LOG_E("th1 rt_thread_create failed...\n");
  43. return -ENOMEM;
  44. }
  45. th2 = rt_thread_create("th2", th2_entry,RT_NULL,512, 20, 5);
  46. if(th1 == RT_NULL){
  47. LOG_E("th2 rt_thread_create failed...\n");
  48. return -ENOMEM;
  49. }
  50. rt_thread_startup(th1);
  51. rt_thread_startup(th2);
  52. return 0;
  53. }

3、事件集

事件集也是线程间同步的机制之一,一个事件集可以包含多个事件,利用事件集可以完成一对多,多对多的线程间同步。
一个线程和多个事件的关系可设置为:
其中任意一个事件唤醒 线程,或几个事件都到达后唤醒线程,多个事件集合可以用一个32bit无符号整型变量来表示,变量的每一位代表一个事件,线程通过”逻辑与”或”逻辑或”将一个或多个事件关联起来,形成事件组合。
RT-Thread 定义的事件集有以下特点:
☐ 事件只与线程相关,事件间相互独立
☐ 事件仅用于同步,不提供数据传输功能
☐ 事件无排队性,即多次向线程发送同一事件(如果线程还未来得及读走),其效果等同于只发送一次
对一个事件集的操作包含:创建/初始化事件集、发送事件、接收事件、删除/脱离事件集。
image.png

  1. #include <rtthread.h>
  2. #define DBG_TAG "main"
  3. #define DBG_LVL DBG_LOG
  4. #include <rtdbg.h>
  5. rt_event_t event1;
  6. rt_thread_t th1 , th2 ,th3; //动态创建
  7. #define EVENT_FLAGS_1 (0x1 << 1)
  8. #define EVENT_FLAGS_2 (0x1 << 2)
  9. #define EVENT_FLAGS_3 (0x1 << 3)
  10. void th1_entry(void *parameter)
  11. {
  12. rt_thread_mdelay(1000);
  13. while(1){
  14. rt_event_recv(event1, EVENT_FLAGS_1,RT_EVENT_FLAG_AND|RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, NULL);
  15. rt_kprintf("th1_entry...\n");
  16. rt_thread_mdelay(1000);
  17. rt_event_send(event1, EVENT_FLAGS_2);
  18. }
  19. }
  20. void th2_entry(void *parameter)
  21. {
  22. rt_thread_mdelay(500);
  23. while(1){
  24. rt_event_recv(event1, EVENT_FLAGS_2,RT_EVENT_FLAG_AND|RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, NULL);
  25. rt_kprintf("th2_entry...\n");
  26. rt_thread_mdelay(1000);
  27. rt_event_send(event1, EVENT_FLAGS_3);
  28. }
  29. }
  30. void th3_entry(void *parameter)
  31. {
  32. int ret = 0;
  33. while(1){
  34. ret = rt_event_recv(event1, EVENT_FLAGS_3,RT_EVENT_FLAG_AND|RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, NULL);
  35. rt_kprintf("th3_entry[%d]...\n",ret);
  36. rt_thread_mdelay(1000);
  37. rt_event_send(event1, EVENT_FLAGS_1);
  38. }
  39. }
  40. int main(void)
  41. {
  42. event1 = rt_event_create("set1", RT_IPC_FLAG_FIFO);
  43. if(event1 == RT_NULL){
  44. LOG_E("rt_event_create failed...\n");
  45. return -ENOMEM;
  46. }
  47. LOG_D("rt_event_create successed...\n");
  48. th1 = rt_thread_create("th1", th1_entry, NULL, 1024, 20, 5);
  49. if(th1 == RT_NULL){
  50. LOG_E("rt_thread_create failed...\n");
  51. return -ENOMEM;
  52. }
  53. th2 = rt_thread_create("th2", th2_entry, NULL, 1024, 20, 5);
  54. if(th2 == RT_NULL){
  55. LOG_E("rt_thread_create failed...\n");
  56. return -ENOMEM;
  57. }
  58. th3 = rt_thread_create("th3", th3_entry, NULL, 1024, 20, 5);
  59. if(th3 == RT_NULL){
  60. LOG_E("rt_thread_create failed...\n");
  61. return -ENOMEM;
  62. }
  63. rt_thread_startup(th1);
  64. rt_thread_startup(th2);
  65. rt_thread_startup(th3);
  66. rt_thread_mdelay(5000);
  67. rt_event_send(event1, EVENT_FLAGS_1);
  68. return 0;
  69. }