网站建设工作分工,平台推广方案设计思路,网站定制化开发介绍,wordpress 视频展示线程间同步 一、信号量1. 创建信号量2. 获取信号量3. 释放信号量4. 删除信号量5. 代码示例 二、互斥量1. 创建互斥量2. 获取互斥量3. 释放互斥量4. 删除互斥量5. 代码示例 三、事件集1. 创建事件集2. 发送事件3. 接收事件4. 删除事件集5. 代码示例 简单来说#xff0c;同步就是… 线程间同步 一、信号量1. 创建信号量2. 获取信号量3. 释放信号量4. 删除信号量5. 代码示例 二、互斥量1. 创建互斥量2. 获取互斥量3. 释放互斥量4. 删除互斥量5. 代码示例 三、事件集1. 创建事件集2. 发送事件3. 接收事件4. 删除事件集5. 代码示例 简单来说同步就是多个线程同时访问一块内存好比如 一个线程向指定内存中写入一个数据另一个线程就从该内存中读取数据这就是“同步”。 线程的同步方式有很多种其核心思想都是在访问临界区的时候只允许一个 (或一类) 线程运行。
以下都以动态创建方式介绍
一、信号量
信号量是一种轻型的用于解决线程间同步问题的内核对象线程可以获取或释放它从而达到同步或互斥的目的。
每个信号量对象都有一个信号量值和一个线程等待队列信号量的值对应了信号量对象的实例数目、资源数目假如信号量值为 5则表示共有 5 个信号量实例资源可以被使用当信号量实例数目为零时再申请该信号量的线程就会被挂起在该信号量的等待队列上等待可用的信号量实例资源。
1. 创建信号量
当创建一个信号量时内核首先创建一个信号量控制块然后对该控制块进行基本的初始化工作 rt_sem_t rt_sem_create(const char *name,rt_uint32_t value,rt_uint8_t flag);注 RT_IPC_FLAG_FIFO先进先出方式时那么等待线程队列将按照先进先出的方式排队先进入的线程将先获得等待的信号量当选择 RT_IPC_FLAG_PRIO优先级等待方式时等待线程队列将按照优先级进行排队优先级高的等待线程将先获得等待的信号量。。
2. 获取信号量
线程通过获取信号量来获得信号量资源实例当信号量值大于零时线程将获得信号量并且相应的信号量值会减 1
rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time);在调用这个函数时如果信号量的值等于零那么说明当前信号量资源实例不可用申请该信号量的线程将根据 time 参数的情况选择直接返回、或挂起等待一段时间、或永久等待直到其他线程或中断释放该信号量。如果在参数 time 指定的时间内依然得不到信号量线程将超时返回返回值是 - RT_ETIMEOUT。
3. 释放信号量
释放信号量可以唤醒挂起在该信号量上的线程
rt_err_t rt_sem_release(rt_sem_t sem);当信号量的值等于零时并且有线程等待这个信号量时释放信号量将唤醒等待在该信号量线程队列中的第一个线程由它获取信号量否则将把信号量的值加 1
4. 删除信号量
系统不再使用信号量时可通过删除信号量以释放系统资源适用于动态创建的信号量
rt_err_t rt_sem_delete(rt_sem_t sem);调用这个函数时系统将删除这个信号量。如果删除该信号量时有线程正在等待该信号量那么删除操作会先唤醒等待在该信号量上的线程等待线程的返回值是 - RT_ERROR然后再释放信号量的内存资源
5. 代码示例
这是一个信号量使用例程该例程创建了一个动态信号量初始化两个线程一个线程发送信号量一个线程接收到信号量后执行相应的操作
#include rtthread.h#define THREAD_PRIORITY 25
#define THREAD_TIMESLICE 5/* 指向信号量的指针 */
static rt_sem_t dynamic_sem RT_NULL;ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
static void rt_thread1_entry(void *parameter)
{static rt_uint8_t count 0;while(1){if(count 100){count;}elsereturn;/* count 每计数 10 次就释放一次信号量 */if(0 (count % 10)){rt_kprintf(t1 release a dynamic semaphore.\n);rt_sem_release(dynamic_sem);}}
}ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
static void rt_thread2_entry(void *parameter)
{static rt_err_t result;static rt_uint8_t number 0;while(1){/* 永久方式等待信号量获取到信号量则执行 number 自加的操作 */result rt_sem_take(dynamic_sem, RT_WAITING_FOREVER);if (result ! RT_EOK){rt_kprintf(t2 take a dynamic semaphore, failed.\n);rt_sem_delete(dynamic_sem);return;}else{number;rt_kprintf(t2 take a dynamic semaphore. number %d\n ,number);}}
}/* 信号量示例的初始化 */
int semaphore_sample(void)
{/* 创建一个动态信号量初始值是 0 */dynamic_sem rt_sem_create(dsem, 0, RT_IPC_FLAG_PRIO);if (dynamic_sem RT_NULL){rt_kprintf(create dynamic semaphore failed.\n);return -1;}else{rt_kprintf(create done. dynamic semaphore value 0.\n);}rt_thread_init(thread1,thread1,rt_thread1_entry,RT_NULL,thread1_stack[0],sizeof(thread1_stack),THREAD_PRIORITY, THREAD_TIMESLICE);rt_thread_startup(thread1);rt_thread_init(thread2,thread2,rt_thread2_entry,RT_NULL,thread2_stack[0],sizeof(thread2_stack),THREAD_PRIORITY-1, THREAD_TIMESLICE);rt_thread_startup(thread2);return 0;
}
/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(semaphore_sample, semaphore sample);运行结果如下 \ | /
- RT - Thread Operating System/ | \ 3.1.0 build Aug 27 20182006 - 2018 Copyright by rt-thread team
msh semaphore_sample
create done. dynamic semaphore value 0.
msh t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number 1
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number 2
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number 3
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number 4
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number 5
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number 6
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number 7
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number 8
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number 9
t1 release a dynamic semaphore.
t2 take a dynamic semaphore. number 10
二、互斥量
互斥量又叫相互排斥的信号量是一种特殊的二值信号量。互斥量类似于只有一个车位的停车场当有一辆车进入的时候将停车场大门锁住其他车辆在外面等候。当里面的车出来时将停车场大门打开下一辆车才可以进入。
互斥量和信号量不同的是拥有互斥量的线程拥有互斥量的所有权互斥量支持递归访问且能防止线程优先级翻转并且互斥量只能由持有线程释放而信号量则可以由任何线程释放。
互斥量的状态只有两种开锁或闭锁两种状态值。当有线程持有它时互斥量处于闭锁状态由这个线程获得它的所有权。相反当这个线程释放它时将对互斥量进行开锁失去它的所有权。当一个线程持有互斥量时其他线程将不能够对它进行开锁或持有它持有该互斥量的线程也能够再次获得这个锁而不被挂起
在 RT-Thread 操作系统中互斥量可以解决优先级翻转问题实现的是优先级继承协议 (Sha, 1990)。优先级继承是通过在线程 A 尝试获取共享资源而被挂起的期间内将线程 C 的优先级提升到线程 A 的优先级别从而解决优先级翻转引起的问题。这样能够防止 C间接地防止 A被 B 抢占如下图所示。优先级继承是指提高某个占有某种资源的低优先级线程的优先级使之与所有等待该资源的线程中优先级最高的那个线程的优先级相等然后执行而当这个低优先级线程释放该资源时优先级重新回到初始设定。因此继承优先级的线程避免了系统资源被任何中间优先级的线程抢占。
1. 创建互斥量
创建一个互斥量时内核首先创建一个互斥量控制块然后完成对该控制块的初始化工作
rt_mutex_t rt_mutex_create (const char* name, rt_uint8_t flag);注 互斥量的 flag 标志已经作废无论用户选择 RT_IPC_FLAG_PRIO 还是 RT_IPC_FLAG_FIFO内核均按照 RT_IPC_FLAG_PRIO 处理
2. 获取互斥量
线程获取了互斥量那么线程就有了对该互斥量的所有权即某一个时刻一个互斥量只能被一个线程持有
rt_err_t rt_mutex_take (rt_mutex_t mutex, rt_int32_t time);如果互斥量没有被其他线程控制那么申请该互斥量的线程将成功获得该互斥量。如果互斥量已经被当前线程线程控制则该互斥量的持有计数加 1当前线程也不会挂起等待。如果互斥量已经被其他线程占有则当前线程在该互斥量上挂起等待直到其他线程释放它或者等待时间超过指定的超时时间
3. 释放互斥量
当线程完成互斥资源的访问后应尽快释放它占据的互斥量使得其他线程能及时获取该互斥量
rt_err_t rt_mutex_release(rt_mutex_t mutex);使用该函数接口时只有已经拥有互斥量控制权的线程才能释放它每释放一次该互斥量它的持有计数就减 1。当该互斥量的持有计数为零时即持有线程已经释放所有的持有操作它变为可用等待在该信号量上的线程将被唤醒。如果线程的运行优先级被互斥量提升那么当互斥量被释放后线程恢复为持有互斥量前的优先级
4. 删除互斥量
当不再使用互斥量时通过删除互斥量以释放系统资源适用于动态创建的互斥量
rt_err_t rt_mutex_delete (rt_mutex_t mutex);当删除一个互斥量时所有等待此互斥量的线程都将被唤醒等待线程获得的返回值是 - RT_ERROR。然后系统将该互斥量从内核对象管理器链表中删除并释放互斥量占用的内存空间
5. 代码示例
这是一个互斥量的应用例程互斥锁是一种保护共享资源的方法。当一个线程拥有互斥锁的时候可以保护共享资源不被其他线程破坏。下面用一个例子来说明有两个线程线程 1 和线程 2线程 1 对 2 个 number 分别进行加 1 操作线程 2 也对 2 个 number 分别进行加 1 操作使用互斥量保证线程改变 2 个 number 值的操作不被打断
互斥量例程
#include rtthread.h#define THREAD_PRIORITY 8
#define THREAD_TIMESLICE 5/* 指向互斥量的指针 */
static rt_mutex_t dynamic_mutex RT_NULL;
static rt_uint8_t number1,number2 0;ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;
static void rt_thread_entry1(void *parameter)
{while(1){/* 线程 1 获取到互斥量后先后对 number1、number2 进行加 1 操作然后释放互斥量 */rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);number1;rt_thread_mdelay(10);number2;rt_mutex_release(dynamic_mutex);}
}ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;
static void rt_thread_entry2(void *parameter)
{while(1){/* 线程 2 获取到互斥量后检查 number1、number2 的值是否相同相同则表示 mutex 起到了锁的作用 */rt_mutex_take(dynamic_mutex, RT_WAITING_FOREVER);if(number1 ! number2){rt_kprintf(not protect.number1 %d, mumber2 %d \n,number1 ,number2);}else{rt_kprintf(mutex protect ,number1 mumber2 is %d\n,number1);}number1;number2;rt_mutex_release(dynamic_mutex);if(number150)return;}
}/* 互斥量示例的初始化 */
int mutex_sample(void)
{/* 创建一个动态互斥量 */dynamic_mutex rt_mutex_create(dmutex, RT_IPC_FLAG_PRIO);if (dynamic_mutex RT_NULL){rt_kprintf(create dynamic mutex failed.\n);return -1;}rt_thread_init(thread1,thread1,rt_thread_entry1,RT_NULL,thread1_stack[0],sizeof(thread1_stack),THREAD_PRIORITY, THREAD_TIMESLICE);rt_thread_startup(thread1);rt_thread_init(thread2,thread2,rt_thread_entry2,RT_NULL,thread2_stack[0],sizeof(thread2_stack),THREAD_PRIORITY-1, THREAD_TIMESLICE);rt_thread_startup(thread2);return 0;
}/* 导出到 MSH 命令列表中 */
MSH_CMD_EXPORT(mutex_sample, mutex sample);
运行结果如下 \ | /
- RT - Thread Operating System/ | \ 3.1.0 build Aug 24 20182006 - 2018 Copyright by rt-thread team
msh mutex_sample
msh mutex protect ,number1 mumber2 is 1
mutex protect ,number1 mumber2 is 2
mutex protect ,number1 mumber2 is 3
mutex protect ,number1 mumber2 is 4
…
mutex protect ,number1 mumber2 is 48
mutex protect ,number1 mumber2 is 49
防止优先级翻转特性例程
#include rtthread.h/* 指向线程控制块的指针 */
static rt_thread_t tid1 RT_NULL;
static rt_thread_t tid2 RT_NULL;
static rt_thread_t tid3 RT_NULL;
static rt_mutex_t mutex RT_NULL;#define THREAD_PRIORITY 10
#define THREAD_STACK_SIZE 512
#define THREAD_TIMESLICE 5/* 线程 1 入口 */
static void thread1_entry(void *parameter)
{/* 先让低优先级线程运行 */rt_thread_mdelay(100);/* 此时 thread3 持有 mutex并且 thread2 等待持有 mutex *//* 检查 thread2 与 thread3 的优先级情况 */if (tid2-current_priority ! tid3-current_priority){/* 优先级不相同测试失败 */rt_kprintf(the priority of thread2 is: %d\n, tid2-current_priority);rt_kprintf(the priority of thread3 is: %d\n, tid3-current_priority);rt_kprintf(test failed.\n);return;}else{rt_kprintf(the priority of thread2 is: %d\n, tid2-current_priority);rt_kprintf(the priority of thread3 is: %d\n, tid3-current_priority);rt_kprintf(test OK.\n);}
}/* 线程 2 入口 */
static void thread2_entry(void *parameter)
{rt_err_t result;rt_kprintf(the priority of thread2 is: %d\n, tid2-current_priority);/* 先让低优先级线程运行 */rt_thread_mdelay(50);/** 试图持有互斥锁此时 thread3 持有应把 thread3 的优先级提升* 到 thread2 相同的优先级*/result rt_mutex_take(mutex, RT_WAITING_FOREVER);if (result RT_EOK){/* 释放互斥锁 */rt_mutex_release(mutex);}
}/* 线程 3 入口 */
static void thread3_entry(void *parameter)
{rt_tick_t tick;rt_err_t result;rt_kprintf(the priority of thread3 is: %d\n, tid3-current_priority);result rt_mutex_take(mutex, RT_WAITING_FOREVER);if (result ! RT_EOK){rt_kprintf(thread3 take a mutex, failed.\n);}/* 做一个长时间的循环500ms */tick rt_tick_get();while (rt_tick_get() - tick (RT_TICK_PER_SECOND / 2)) ;rt_mutex_release(mutex);
}int pri_inversion(void)
{/* 创建互斥锁 */mutex rt_mutex_create(mutex, RT_IPC_FLAG_PRIO);if (mutex RT_NULL){rt_kprintf(create dynamic mutex failed.\n);return -1;}/* 创建线程 1 */tid1 rt_thread_create(thread1,thread1_entry,RT_NULL,THREAD_STACK_SIZE,THREAD_PRIORITY - 1, THREAD_TIMESLICE);if (tid1 ! RT_NULL)rt_thread_startup(tid1);/* 创建线程 2 */tid2 rt_thread_create(thread2,thread2_entry,RT_NULL,THREAD_STACK_SIZE,THREAD_PRIORITY, THREAD_TIMESLICE);if (tid2 ! RT_NULL)rt_thread_startup(tid2);/* 创建线程 3 */tid3 rt_thread_create(thread3,thread3_entry,RT_NULL,THREAD_STACK_SIZE,THREAD_PRIORITY 1, THREAD_TIMESLICE);if (tid3 ! RT_NULL)rt_thread_startup(tid3);return 0;
}/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(pri_inversion, prio_inversion sample);
运行结果如下 \ | /
- RT - Thread Operating System/ | \ 3.1.0 build Aug 27 20182006 - 2018 Copyright by rt-thread team
msh pri_inversion
the priority of thread2 is: 10
the priority of thread3 is: 11
the priority of thread2 is: 10
the priority of thread3 is: 10
test OK.
三、事件集
事件集主要用于线程间的同步与信号量不同它的特点是可以实现一对多多对多的同步。即一个线程与多个事件的关系可设置为其中任意一个事件唤醒线程或几个事件都到达后才唤醒线程进行后续的处理同样事件也可以是多个线程同步多个事件。这种多个事件的集合可以用一个 32 位无符号整型变量来表示变量的每一位代表一个事件线程通过 “逻辑与” 或“逻辑或”将一个或多个事件关联起来形成事件组合。事件的 “逻辑或” 也称为是独立型同步指的是线程与任何事件之一发生同步事件 “逻辑与” 也称为是关联型同步指的是线程与若干事件都发生同步。
RT-Thread 定义的事件集有以下特点
事件只与线程相关事件间相互独立每个线程可拥有 32 个事件标志采用一个 32 bit 无符号整型数进行记录每一个 bit 代表一个事件事件仅用于同步不提供数据传输功能事件无排队性即多次向线程发送同一事件 (如果线程还未来得及读走)其效果等同于只发送一次。
在 RT-Thread 中每个线程都拥有一个事件信息标记它有三个属性分别是 RT_EVENT_FLAG_AND(逻辑与)RT_EVENT_FLAG_OR(逻辑或以及 RT_EVENT_FLAG_CLEAR(清除标记。当线程等待事件同步时可以通过 32 个事件标志和这个事件信息标记来判断当前接收的事件是否满足同步条件。 线程 #1 的事件标志中第 1 位和第 30 位被置位如果事件信息标记位设为逻辑与则表示线程 #1 只有在事件 1 和事件 30 都发生以后才会被触发唤醒如果事件信息标记位设为逻辑或则事件 1 或事件 30 中的任意一个发生都会触发唤醒线程 #1。如果信息标记同时设置了清除标记位则当线程 #1 唤醒后将主动把事件 1 和事件 30 清为零否则事件标志将依然存在即置 1
1. 创建事件集
当创建一个事件集时内核首先创建一个事件集控制块然后对该事件集控制块进行基本的初始化
rt_event_t rt_event_create(const char* name, rt_uint8_t flag);调用该函数接口时系统会从对象管理器中分配事件集对象并初始化这个对象然后初始化父类 IPC 对象
2. 发送事件
发送事件函数可以发送事件集中的一个或多个事件
rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set);使用该函数接口时通过参数 set 指定的事件标志来设定 event 事件集对象的事件标志值然后遍历等待在 event 事件集对象上的等待线程链表判断是否有线程的事件激活要求与当前 event 对象事件标志值匹配如果有则唤醒该线程
3. 接收事件
内核使用 32 位的无符号整数来标识事件集它的每一位代表一个事件因此一个事件集对象可同时等待接收 32 个事件内核可以通过指定选择参数 “逻辑与” 或“逻辑或”来选择如何激活线程使用 “逻辑与” 参数表示只有当所有等待的事件都发生时才激活线程而使用 “逻辑或” 参数则表示只要有一个等待的事件发生就激活线程
rt_err_t rt_event_recv(rt_event_t event,rt_uint32_t set,rt_uint8_t option,rt_int32_t timeout,rt_uint32_t* recved);当用户调用这个接口时系统首先根据 set 参数和接收选项 option 来判断它要接收的事件是否发生如果已经发生则根据参数 option 上是否设置有 RT_EVENT_FLAG_CLEAR 来决定是否重置事件的相应标志位然后返回其中 recved 参数返回接收到的事件如果没有发生则把等待的 set 和 option 参数填入线程本身的结构中然后把线程挂起在此事件上直到其等待的事件满足条件或等待时间超过指定的超时时间。如果超时时间设置为零则表示当线程要接受的事件没有满足其要求时就不等待而直接返回 - RT_ETIMEOUT option 的值可取
/* 选择 逻辑与 或 逻辑或 的方式接收事件 */
RT_EVENT_FLAG_OR
RT_EVENT_FLAG_AND/* 选择清除重置事件标志位 */
RT_EVENT_FLAG_CLEAR
4. 删除事件集
系统不再使用 rt_event_create() 创建的事件集对象时通过删除事件集对象控制块来释放系统资源
rt_err_t rt_event_delete(rt_event_t event);5. 代码示例
这是事件集的应用例程例子中初始化了一个事件集两个线程。一个线程等待自己关心的事件发生另外一个线程发送事件
#include rtthread.h#define THREAD_PRIORITY 9
#define THREAD_TIMESLICE 5#define EVENT_FLAG3 (1 3)
#define EVENT_FLAG5 (1 5)/* 事件控制块 */
static struct rt_event event;ALIGN(RT_ALIGN_SIZE)
static char thread1_stack[1024];
static struct rt_thread thread1;/* 线程 1 入口函数 */
static void thread1_recv_event(void *param)
{rt_uint32_t e;/* 第一次接收事件事件 3 或事件 5 任意一个可以触发线程 1接收完后清除事件标志 */if (rt_event_recv(event, (EVENT_FLAG3 | EVENT_FLAG5),RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,RT_WAITING_FOREVER, e) RT_EOK){rt_kprintf(thread1: OR recv event 0x%x\n, e);}rt_kprintf(thread1: delay 1s to prepare the second event\n);rt_thread_mdelay(1000);/* 第二次接收事件事件 3 和事件 5 均发生时才可以触发线程 1接收完后清除事件标志 */if (rt_event_recv(event, (EVENT_FLAG3 | EVENT_FLAG5),RT_EVENT_FLAG_AND | RT_EVENT_FLAG_CLEAR,RT_WAITING_FOREVER, e) RT_EOK){rt_kprintf(thread1: AND recv event 0x%x\n, e);}rt_kprintf(thread1 leave.\n);
}ALIGN(RT_ALIGN_SIZE)
static char thread2_stack[1024];
static struct rt_thread thread2;/* 线程 2 入口 */
static void thread2_send_event(void *param)
{rt_kprintf(thread2: send event3\n);rt_event_send(event, EVENT_FLAG3);rt_thread_mdelay(200);rt_kprintf(thread2: send event5\n);rt_event_send(event, EVENT_FLAG5);rt_thread_mdelay(200);rt_kprintf(thread2: send event3\n);rt_event_send(event, EVENT_FLAG3);rt_kprintf(thread2 leave.\n);
}int event_sample(void)
{rt_err_t result;/* 初始化事件对象 */result rt_event_init(event, event, RT_IPC_FLAG_PRIO);if (result ! RT_EOK){rt_kprintf(init event failed.\n);return -1;}rt_thread_init(thread1,thread1,thread1_recv_event,RT_NULL,thread1_stack[0],sizeof(thread1_stack),THREAD_PRIORITY - 1, THREAD_TIMESLICE);rt_thread_startup(thread1);rt_thread_init(thread2,thread2,thread2_send_event,RT_NULL,thread2_stack[0],sizeof(thread2_stack),THREAD_PRIORITY, THREAD_TIMESLICE);rt_thread_startup(thread2);return 0;
}/* 导出到 msh 命令列表中 */
MSH_CMD_EXPORT(event_sample, event sample);
运行结果如下 \ | /
- RT - Thread Operating System/ | \ 3.1.0 build Aug 24 20182006 - 2018 Copyright by rt-thread team
msh event_sample
thread2: send event3
thread1: OR recv event 0x8
thread1: delay 1s to prepare the second event
msh thread2: send event5
thread2: send event3
thread2 leave.
thread1: AND recv event 0x28
thread1 leave.