1、信号量
信号量是一种轻型的用于解决线程间同步问题的内核对象,线程可以获取或释放它,从而达到同步或互斥的目的。
每个信号量对象都有一个信号量值和一个线程等待队列,信号量的值对应了信号量对象的实例数目、资源数目,假如信号量值为 5,则表示共有 5 个信号量实例(资源)可以被使用,当信号量实例数目为零时,再申请该信号量的线程就会被挂起在该信号量的等待队列上,等待可用的信号量实例(资源)
1.1创建和删除信号量
#define DBG_TAG "main"#define DBG_LVL DBG_LOG#include <rtdbg.h>rt_sem_t semout1 = NULL;int main(void){//名字小于八个字符semout1 = rt_sem_create("sem1", 1, RT_IPC_FLAG_FIFO);if(semout1 == NULL){LOG_E("semout1 is error.......\n");return -ENOMEM;}else {LOG_E("semout1 is successful.......\n");}//rt_sem_delete(semout1);return 0;}
1.2初始化和脱离信号量
#define DBG_TAG "main"#define DBG_LVL DBG_LOG#include <rtdbg.h>struct rt_semaphore sem2;int main(void){int ret2 = 0;ret2 = rt_sem_init(&sem2, "sem2", 2, RT_IPC_FLAG_FIFO);//如果直接定义指针就只有一个指针的大小;if(ret2 < 0){LOG_E("sem2 is error.......\n");return ret2;}else {LOG_E("sem2 is successful.......\n");}//rt_sem_detach(&sem2);return 0;}
1.3获取释放信号量
线程通过获取信号量来获得信号量资源实例,当信号量值大于零时,线程将获得信号量,并且相应的信号量值会减 1,如果信号量的值等于零,那么说明当前信号量资源实例不可用,申请该信号量的线程将根据time参数的情况选择直接返回、或挂起等待一段时间、或永久等待,直到其他线程或中断释放该信号量。如果在参数time指定的时间内依然得不到信号量,线程将超时返回,返回值是 - RT_ETIMEOUT。
#include <rtthread.h>#define DBG_TAG "main"#define DBG_LVL DBG_LOG#include <rtdbg.h>rt_sem_t semout1 = NULL;struct rt_semaphore sem2;int flags = 0;rt_thread_t rtt_ntr1 = NULL;rt_thread_t rtt_ntr2 = NULL;void th_entry1(void *parameter){while(1){rt_thread_mdelay(800);rt_sem_take(semout1, RT_WAITING_FOREVER);flags++;if(flags == 100){flags = 0;}rt_kprintf("th_entry1 is running and the flags = %d.......\n\r",flags);rt_sem_release(&sem2);}}void th_entry2(void *parameter){while(1){rt_sem_take(&sem2, RT_WAITING_FOREVER);flags--;rt_kprintf("th_entry2 is running and the flags = %d.......\n\r",flags);rt_sem_release(semout1);rt_thread_mdelay(100);}}int main(void){//名字小于八个字符semout1 = rt_sem_create("sem1", 1, RT_IPC_FLAG_FIFO);if(semout1 == NULL){LOG_E("semout1 is error.......\n");return -ENOMEM;}else {LOG_E("semout1 is successful.......\n");}//rt_sem_delete(semout1);int ret2 = 0;ret2 = rt_sem_init(&sem2, "sem2", 0, RT_IPC_FLAG_FIFO);//如果直接定义指针就只有一个指针的大小;if(ret2 < 0){LOG_E("sem2 is error.......\n");return ret2;}else {LOG_E("sem2 is successful.......\n");}//rt_sem_detach(&sem2);rtt_ntr1 = rt_thread_create("test",th_entry1,NULL,512,20,5);if(rtt_ntr1 == RT_NULL){LOG_E("rt_thread_create1 error .....\n");return -RT_ENOMEM;}else {LOG_E("rt_thread_create1 successful .....\n");rt_thread_startup(rtt_ntr1);}rtt_ntr2 = rt_thread_create("test",th_entry2,NULL,512,20,5);if(rtt_ntr2 == RT_NULL){LOG_E("rt_thread_create2 error .....\n");return -RT_ENOMEM;}else {LOG_E("rt_thread_create2 successful .....\n");rt_thread_startup(rtt_ntr2);}return 0;}
2、互斥量
互斥量体现的是排他性,也是解决多线程同时操作临界区临界资源导致的竟态的一种方法。(类似于特殊的信号量——二值信号量)
区别:信号量可由不同线程释放,互斥量只能由同一线程进行释放。
rt_mutex_t mux1 = NULL;mux1 = rt_mutex_create("mutex1", RT_IPC_FLAG_FIFO);if(mux1 == NULL){LOG_E("mutex1 is error.......\n");return -ENOMEM;}else {LOG_E("mutex1 is successful.......\n");}//rt_mutex_delete(mux1);
示例:
#include <rtthread.h>#define DBG_TAG "main"#define DBG_LVL DBG_LOG#include <rtdbg.h>rt_mutex_t mutex1 ;struct rt_mutex mutex2;rt_thread_t th1 , th2;int flag1 = 0, flag2 = 0;void th1_entry(void *parameter){while(1){rt_mutex_take(mutex1, RT_WAITING_FOREVER);flag1++;rt_thread_mdelay(1000);flag2++;rt_kprintf("th1_entry flag1:%d,flag2:%d\n",flag1,flag2);rt_mutex_release(mutex1);}}void th2_entry(void *parameter){while(1){rt_mutex_take(mutex1, RT_WAITING_FOREVER);flag1++;flag2++;rt_mutex_release(mutex1);rt_kprintf("th2_entry flag1:%d,flag2:%d\n",flag1,flag2);rt_thread_mdelay(1000);}}int main(void){//动态创建互斥锁mutex1 = rt_mutex_create("mutex1", RT_IPC_FLAG_FIFO);if(mutex1 == RT_NULL){LOG_E("mutex1 rt_mutex_create failed...\n");return -ENOMEM;}LOG_D("rt_mutex_create successed...\n");th1 = rt_thread_create("th1", th1_entry,RT_NULL,512, 20, 5);if(th1 == RT_NULL){LOG_E("th1 rt_thread_create failed...\n");return -ENOMEM;}th2 = rt_thread_create("th2", th2_entry,RT_NULL,512, 20, 5);if(th1 == RT_NULL){LOG_E("th2 rt_thread_create failed...\n");return -ENOMEM;}rt_thread_startup(th1);rt_thread_startup(th2);return 0;}
3、事件集
事件集也是线程间同步的机制之一,一个事件集可以包含多个事件,利用事件集可以完成一对多,多对多的线程间同步。
一个线程和多个事件的关系可设置为:
其中任意一个事件唤醒 线程,或几个事件都到达后唤醒线程,多个事件集合可以用一个32bit无符号整型变量来表示,变量的每一位代表一个事件,线程通过”逻辑与”或”逻辑或”将一个或多个事件关联起来,形成事件组合。
RT-Thread 定义的事件集有以下特点:
☐ 事件只与线程相关,事件间相互独立
☐ 事件仅用于同步,不提供数据传输功能
☐ 事件无排队性,即多次向线程发送同一事件(如果线程还未来得及读走),其效果等同于只发送一次
对一个事件集的操作包含:创建/初始化事件集、发送事件、接收事件、删除/脱离事件集。
#include <rtthread.h>#define DBG_TAG "main"#define DBG_LVL DBG_LOG#include <rtdbg.h>rt_event_t event1;rt_thread_t th1 , th2 ,th3; //动态创建#define EVENT_FLAGS_1 (0x1 << 1)#define EVENT_FLAGS_2 (0x1 << 2)#define EVENT_FLAGS_3 (0x1 << 3)void th1_entry(void *parameter){rt_thread_mdelay(1000);while(1){rt_event_recv(event1, EVENT_FLAGS_1,RT_EVENT_FLAG_AND|RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, NULL);rt_kprintf("th1_entry...\n");rt_thread_mdelay(1000);rt_event_send(event1, EVENT_FLAGS_2);}}void th2_entry(void *parameter){rt_thread_mdelay(500);while(1){rt_event_recv(event1, EVENT_FLAGS_2,RT_EVENT_FLAG_AND|RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, NULL);rt_kprintf("th2_entry...\n");rt_thread_mdelay(1000);rt_event_send(event1, EVENT_FLAGS_3);}}void th3_entry(void *parameter){int ret = 0;while(1){ret = rt_event_recv(event1, EVENT_FLAGS_3,RT_EVENT_FLAG_AND|RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, NULL);rt_kprintf("th3_entry[%d]...\n",ret);rt_thread_mdelay(1000);rt_event_send(event1, EVENT_FLAGS_1);}}int main(void){event1 = rt_event_create("set1", RT_IPC_FLAG_FIFO);if(event1 == RT_NULL){LOG_E("rt_event_create failed...\n");return -ENOMEM;}LOG_D("rt_event_create successed...\n");th1 = rt_thread_create("th1", th1_entry, NULL, 1024, 20, 5);if(th1 == RT_NULL){LOG_E("rt_thread_create failed...\n");return -ENOMEM;}th2 = rt_thread_create("th2", th2_entry, NULL, 1024, 20, 5);if(th2 == RT_NULL){LOG_E("rt_thread_create failed...\n");return -ENOMEM;}th3 = rt_thread_create("th3", th3_entry, NULL, 1024, 20, 5);if(th3 == RT_NULL){LOG_E("rt_thread_create failed...\n");return -ENOMEM;}rt_thread_startup(th1);rt_thread_startup(th2);rt_thread_startup(th3);rt_thread_mdelay(5000);rt_event_send(event1, EVENT_FLAGS_1);return 0;}
