线 程 同 步 是 指 多 个 线 程 通 过 某 种 特 定 的 机 制 ,来 控 制 线 程 之 间 的 先
后 执 行 顺 序 。
RT-Thread 提 供 了 一 种 线 程 同 步 的 方 式 : 信 号 量 ( semaphore) 、
互 斥 量( mutex)、和 事 件 集( event)。本 篇 文 章 主 要 介 绍 信 号 量
相 关 的 内 容 。 信 号 量 的 工 作 机 制
信 号 量 是 一 种 可 以 用 来 解 决 线 程 间 同 步 问 题 的 内 核 对 象 ,线 程 通 过
获 取 和 释 放 信 号 量 , 来 达 到 同 步 的 目 的 。
每 个 信 号 量 对 象 都 有 一 个 信 号 量 值 和 一 个 线 程 等 待 队 列 ,信 号 量 的
值 表 示 信 号 对 象 的 实 例 数 目 或 者 资 源 数 目 ;线 程 等 待 队 列 ,由 等 待
获 取 当 前 信 号 量 的 线 程 按 照 某 种 顺 序 排 列 而 成 。
当 信 号 量 值 为 零 时 ,再 申 请 该 信 号 量 的 线 程 就 会 被 挂 起 在 该 信 号 量
的 等 待 队 列 上 , 等 待 可 用 的 信 号 量 资 源 。
信 号 量 控 制 块
信 号 量 控 制 块 是 RT-Thread 用 于 管 理 信 号 量 的 一 个 数 据 结 构 ,信
号 量 控 制 块 的 结 构 体 struc t rt_semaphore 定 义 如 下 ,rt_sem_t 表 示 信
号 量 的 句 柄 , 即 指 向 信 号 量 控 制 块 的 指 针 。
struct rt_semaphore
{
struct rt_ipc_object parent; /* 继承自 ipc_object 类 */
rt_uint16_t value; /* 信号量的值 */
rt_uint16_t reserved; /* 保留域 */
};
/* rt_sem_t 为指向 rt_semaphore 结构体的指针类型 */
typedef struct rt_semaphore *rt_sem_t;
struc t rt_semaphore 从 rt_ipc_objec t 派 生 而 来 , 由 IPC 容 器 管 理 , 信
号 量 的 最 大 值 为 65535。
结 构 体 struc t rt_ipc_objec t parent 定 义 如 下 :
struct rt_object
{
char name[RT_NAME_MAX]; /* 内核对象名称 */
rt_uint8_t type; /* 内核对象类型 */
rt_uint8_t flag; /* 内核对象的参数 */
#ifdef RT_USING_MODULE
void *module_id; /* 应用程序模块 ID */
#endif
rt_list_t list; /* 内核对象管理链表 */
};
struct rt_ipc_object
{
struct rt_object parent; /* 继承自 rt_object */
rt_list_t suspend_thread; /* 挂起的线程链表 */
};
信 号 量 控 制 块 中 含 有 信 号 量 相 关 的 重 要 参 数 ,在 信 号 量 各 种 状 态 之
间 起 到 纽 带 的 作 用 。
接 下 来 看 看 如 何 对 一 个 信 号 量 进 行 操 作 。 管 理 信 号 量
RT-Thread 提 供 了 一 系 列 的 函 数 接 口 , 用 于 对 信 号 量 进 行 操 作 。
包 括 :
创 建 /初 始 化 信 号 量
获 取 信 号 量
释 放 信 号 量
删 除 /脱 离 信 号 量
常 用 的 信 号 量 操 作 为 :创 建 信 号 量 、获 取 信 号 量 、释 放 信 号 量 。下
面 重 点 介 绍 这 三 种 操 作 。
1. 创 建 信 号 量
RT-Thread 创 建 信 号 量 两 种 方 式 : 动 态 创 建 和 静 态 初 始 化 。
跟 其 他 内 核 对 象 类 似 , 动 态 创 建 是 由 内 核 负 责 分 配 信 号 量 控 制 块 ,
然 后 对 其 进 行 基 本 的 初 始 化 工 作 。静 态 方 式 创 建 ,是 由 用 户 负 责 定
义 一 个 信 号 量 控 制 块 结 构 体 变 量 ,然 后 调 用 初 始 化 函 数 对 其 进 行 初
始 化 工 作 。
动 态 创 建 信 号 量 的 函 数 接 口 如 下 :
rt_sem_t rt_sem_create(const char *name,
rt_uint32_t value,
rt_uint8_t flag)
当 调 用 这 个 函 数 时 , 系 统 将 先 从 对 象 管 理 器 中 分 配 一 个
semaphore 对 象 , 并 初 始 化 这 个 对 象 , 然 后 初 始 化 父 类 IPC 对 象
以 及 与 semaphore 相 关 的 部 分 。
该 函 数 的 各 个 参 数 解 释 如 下 :
参 数 描 述
name 信 号 量 名 称
value 信 号 量 的 初 始 值
flag 创 建 信 号 量 的 标 志
信 号 量 创 建 成 功 ,则 返 回 信 号 量 控 制 块 的 指 针 。创 建 失 败 ,则 返 回
RT_NULL。
参 数 flag 的 作 用 是 ,当 信 号 量 不 可 用 时 ,多 个 线 程 等 待 的 排 队 方 式 。
这 个 参 数 取 值 有 两 种 :
RT_IPC_FLAG_FIFO, 先 进 先 出 方 式 。 等 待 信 号 量 的 线 程 按 照 先
进 先 出 的 方 式 排 队 , 先 进 入 的 线 程 将 先 获 得 等 待 的 信 号 量 。
RT_IPC_FLAG_PRIO, 优 先 级 等 待 方 式 。 等 待 信 号 量 的 线 程 按 照
优 先 级 进 行 排 队 , 优 先 级 高 的 等 待 线 程 将 先 获 得 等 待 的 信 号 量 。
静 态 方 式 创 建 信 号 量 , 需 要 先 定 义 一 个 信 号 量 控 制 块 结 构 struc t
rt_semaphore 类 型 的 变 量 , 然 后 使 用 如 下 函 数 对 其 进 行 初 始 化 :
rt_err_t rt_sem_init(rt_sem_t sem,
const char *name,
rt_uint32_t value,
rt_uint8_t flag)
这 个 函 数 参 数 , 除 了 sem , 其 他 参 数 跟 动 态 创 建 信 号 量 函 数
rt_sem_create() 的 参 数 相 同 。
参 数 sem 为 信 号 量 控 制 块 的 指 针 , 指 向 用 户 定 义 的 struc t
rt_semaphore 结 构 变 量 的 地 址 。
rt_sem_init() 函 数 的 主 要 作 用 是 , 对 sem 指 向 的 信 号 量 控 制 块 进 行
初 始 化 操 作 。
该 函 数 的 返 回 值 为 RT_EOK。
2. 获 取 信 号 量
线 程 通 过 获 取 信 号 量 来 获 得 信 号 量 资 源 实 例 ,当 信 号 量 值 大 于 零 时 ,
线 程 将 获 得 信 号 量 ,并 且 相 应 的 信 号 量 值 会 减 1。如 果 信 号 量 的 值
为 零 , 说 明 当 前 信 号 量 资 源 不 可 用 , 线 程 会 获 取 失 败 。
RT-Thread 中 获 取 信 号 量 的 函 数 如 下 :
rt_err_t rt_sem_take (rt_sem_t sem, rt_int32_t time)
参 数 sem 表 示 信 号 量 控 制 块 指 针 ( 信 号 量 的 句 柄 ) 。
参 数 time 表 示 线 程 等 待 获 取 信 号 量 的 时 间 , 单 位 是 系 统 时 钟 节 拍 。
调 用 此 函 数 获 取 信 号 量 时 , 如 果 信 号 量 的 值 为 零 , 线 程 将 根
据 time 参 数 的 情 况 会 有 不 同 的 动 作 :
参 数 值 为 零 , 则 函 数 会 直 接 返 回 。
参 数 值 不 为 零 , 则 会 等 待 设 定 的 时 间 。
参 数 值 为 最 大 时 钟 节 拍 数 ,则 会 永 久 等 待 ,直 到 其 他 线 程 或 中 断 释
放 该 信 号 量 。
如 果 在 参 数 time 指 定 的 时 间 内 没 有 获 取 到 信 号 量 ,线 程 将 超 时 返 回 ,
返 回 值 为 -RT_ETIMEOUT。
rt_sem_take() 函 数 返 回 RT_EOK , 表 示 成 功 获 得 信 号 量 。 返
回 -RT_ERROR , 表 示 其 他 错 误 。
线 程 获 取 信 号 量 不 可 以 用 时 , 且 等 待 时 间 time 不 为 零 ,
3. 释 放 信 号 量
释 放 信 号 量 的 系 统 函 数 如 下 :
rt_err_t rt_sem_release(rt_sem_t sem)
参 数 sem 表 示 信 号 量 控 制 块 指 针 ( 信 号 量 的 句 柄 ) 。
释 放 信 号 量 操 作 , 根 据 具 体 情 况 , 会 有 两 种 结 果 :
如 果 有 线 程 等 待 获 取 这 个 信 号 量 时 ,释 放 信 号 量 将 唤 醒 等 待 队 列 中
的 第 一 个 线 程 , 由 它 获 取 信 号 量 , 信 号 量 的 值 仍 然 为 零 。
如 果 没 有 线 程 等 待 获 取 信 号 量 , 则 信 号 量 的 值 将 会 加 1。
实 战 演 练
绝 知 此 事 要 躬 行 。
通 过 具 体 的 实 例 ,来 看 看 如 何 使 用 RT-Thread 的 信 号 量 操 作 函 数 。
动 态 创 建 一 个 信 号 量 ,创 建 两 个 线 程 ,一 个 线 程 释 放 信 号 量 ,一 个
线 程 获 取 信 号 量 后 , 执 行 后 续 的 动 作 。
#include
#define THREAD_PRIORITY 25
#define THREAD_TIMESLICE 5
/* 指向信号量的指针 */
static rt_sem_t dynamic_sem = RT_NULL;
/* 线程 1 入口函数 */
static void rt_thread1_entry(void *parameter)
{
static rt_uint8_t count = 0;
while(1)
{
if(count <= 100)
{
count++;
}
else
{
return;
}
/* count 每计数 10 次, 就释放一次信号量 */
if(0 == (count % 10))
{
rt_kprintf("thread1 release a dynamic semaphore.\n");
rt_sem_release(dynamic_sem);
}
/* 延迟一会儿 */
rt_thread_delay(10);
}
}
/* 线程 2 入口函数 */
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("thread2 take a dynamic semaphore, failed.\n");
rt_sem_delete(dynamic_sem);
return;
}
else
{
number++;
rt_kprintf("thread2 take a dynamic semaphore. number = %d\n
" ,number);
}
rt_thread_delay(10);
}
}
int main(void)
{
/* 线程控制块指针 */
rt_thread_t thread1 = RT_NULL;
rt_thread_t thread2 = RT_NULL;
/* 创建一个动态信号量,初始值是 0 */
dynamic_sem = rt_sem_create("dsem", 0, RT_IPC_FLAG_FIFO);
if (dynamic_sem == RT_NULL)
{
rt_kprintf("create dynamic semaphore failed.\n");
return -1;
}
else
{
rt_kprintf("create done. dynamic semaphore value = 0.\n");
}
/* 动态创建线程 1 */
thread1 = rt_thread_create("thread1", rt_thread1_entry, RT_NULL,
1024, THREAD_PRIORITY, THREAD_TIMESLICE);
if(thread1 != RT_NULL)
{
/* 启动线程 */
rt_thread_startup(thread1);
}
/* 动态创建线程 2 */
thread2 = rt_thread_create("thread2", rt_thread2_entry, RT_NULL,
1024, THREAD_PRIORITY-1, THREAD_TIMESLICE);
if(thread2 != RT_NULL)
{
/* 启动线程 */
rt_thread_startup(thread2);
}
return 0;
}
线 程 1 在 count 计 数 为 10 的 倍 数 时 ,释 放 一 个 信 号 量 ,线 程 2
在 接 收 到 信 号 量 后 ,对 number 进 行 加 1 操 作 。程 序 运 行 结 果 如 下
所 示 :
部分文件列表
文件名 |
大小 |
RT-Thread快速入门-线程间同步之信号量.pdf |
325K |
全部评论(0)