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;
}