#C 语言标准库函数 mtx_init
/*********************************************
* @brief 创建互斥量
* @param[out] mutex 返回互斥量 ID
* @param type 互斥量类型
* @return 是否成功
********************************************/
int mtx_init(mtx_t* mutex, int type);
说明
创建一个互斥量,通过参数 mutex
返回互斥量 ID。
需要通过 mtx_destroy 函数清除互斥量。
类型 type
可以说以下值:
mtx_plain
- 创建一个简单的互斥量mtx_timed
- 创建一个支持超时的互斥量mtx_plain | mtx_recursive
- 创建一个普通的可递归互斥量mtx_timed | mtx_recursive
- 创建一个支持超时的可递归互斥量
参数
mutex
- 返回互斥量 IDtype
- 互斥量类型
返回值
- 成功时返回
thrd_success
- 失败时返回
thrd_error
#示例
#include <stdio.h>
#include <threads.h>
// 共享数据和互斥量
struct Args
{
int n;
mtx_t mutex;
};
// 线程函数1,不加锁,结果错误
int func1(void* data)
{
struct Args* args = (struct Args*)data;
for (int i = 0; i < 10; i++)
{
int value = args->n + 1;
for (int i = 0; i < 30000; i += 1); // 延长执行时间,提高不加锁时的出错概率
args->n = value;
}
return 0;
}
// 线程函数2,加锁,结果正确
int func2(void* data)
{
struct Args* args = (struct Args*)data;
for (int i = 0; i < 10; i++)
{
mtx_lock(&args->mutex); // 锁定 mutex
int value = args->n + 1;
for (int i = 0; i < 30000; i += 1); // 延长执行时间,提高不加锁时的出错概率
args->n = value;
mtx_unlock(&args->mutex); // 解锁 mutex
}
return 0;
}
int main(void)
{
struct Args data1, data2;
mtx_init(&data2.mutex, mtx_plain); // 创建互斥量
data1.n = 0;
data2.n = 0;
// 创建线程
thrd_t th1, th2, th3, th4;
thrd_create(&th1, func1, &data1); // 通过参数传递数据和互斥量
thrd_create(&th2, func1, &data1);
thrd_create(&th3, func2, &data2);
thrd_create(&th4, func2, &data2);
// 等待线程结束
thrd_join(th1, NULL);
thrd_join(th2, NULL);
thrd_join(th3, NULL);
thrd_join(th4, NULL);
printf("data1 的最终值为 %d \n", data1.n);
printf("data2 的最终值为 %d \n", data2.n);
// 清除互斥量
mtx_destroy(&data2.mutex);
return 0;
}
说明:
示例中的 func1
和 func2
函数都是循环十次将数据的值加 1,最终将数据的值加 10。
由于这两个函数各创建了两个线程,所以正确的结果应该是 20。
但是因为 func1
没有加锁,因此可能产生这样的错误步骤:
data.n
的值是,线程 1 计算 value = data.n + 1
得到值- 发生线程调度,执行线程 2,
data.n
的值仍是,线程 2 计算 value = data.n + 1
得到值 - 线程 1 执行
args->n = value
,data.n
的值被修改为 - 发生线程调度,执行线程 1,线程 1 执行
args->n = value
,data.n
的值被修改为
两个线程各执行一次加 1,原本应该总共将值加 2,但是由于线程调度导致只加了 1。
这样的步骤可能随机发生在任何时候,因此产生了随机的结果。
而 func2
通过互斥量,在计算 value = data.n + 1
之前加锁,执行 args->n = value
后解锁;
保障了步骤的原子性,避免了上述错误的发生。
多次运行的可能结果:
$ ./a.out data1 的最终值为 20 data2 的最终值为 20 $ ./a.out data1 的最终值为 14 data2 的最终值为 20 $ ./a.out data1 的最终值为 20 data2 的最终值为 20 $ ./a.out data1 的最终值为 14 data2 的最终值为 20 $ ./a.out data1 的最终值为 15 data2 的最终值为 20 $ ./a.out data1 的最终值为 13 data2 的最终值为 20 $ ./a.out data1 的最终值为 10 data2 的最终值为 20 $ ./a.out data1 的最终值为 10 data2 的最终值为 20 $ ./a.out data1 的最终值为 11 data2 的最终值为 20
#互斥量类型
互斥量类型 | 说明 |
---|---|
mtx_plain | 简单的互斥量,只能被加锁一次,不支持超时 |
mtx_timed | 支持超时的互斥量,只能被加锁一次,支持超时 |
mtx_plain | mtx_recursive | 普通的可递归互斥量,可以被同一个线程加锁多次,需要解锁相同的次数,不支持超时 |
mtx_timed | mtx_recursive | 支持超时的可递归互斥量,可以被同一个线程加锁多次,需要解锁相同的次数,支持超时 |
#推荐阅读
#参考标准
- C17 standard (ISO/IEC 9899:2018):
- 7.26.4.2 The mtx_init function (p: 277-278)
- C11 standard (ISO/IEC 9899:2011):
- 7.26.4.2 The mtx_init function (p: 381)