4195

21 分钟

#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 - 返回互斥量 ID
  • type - 互斥量类型

返回值

  • 成功时返回 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; }

说明

示例中的 func1func2 函数都是循环十次将数据的值加 1,最终将数据的值加 10。

由于这两个函数各创建了两个线程,所以正确的结果应该是 20。

但是因为 func1 没有加锁,因此可能产生这样的错误步骤:

  1. data.n 的值是 ,线程 1 计算 value = data.n + 1 得到值
  2. 发生线程调度,执行线程 2,data.n 的值仍是 ,线程 2 计算 value = data.n + 1 得到值
  3. 线程 1 执行 args->n = valuedata.n 的值被修改为
  4. 发生线程调度,执行线程 1,线程 1 执行 args->n = valuedata.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)

创建于 2025/8/24

更新于 2025/8/24