Linux 多线程编程:互斥锁(Mutex)详解

在多线程编程中,当多个线程需要访问共享资源时,如果不加以控制,就会导致数据竞争(Data Race),引发程序行为不可预测。互斥锁(Mutex)是解决此类问题的核心同步机制之一。

1. 核心概念

1.1 临界资源(Critical Resource)

一次只允许一个线程访问的共享资源。例如:全局变量、静态变量、堆内存、文件、硬件设备等。

1.2 临界区(Critical Section)

访问临界资源的那段代码。线程进入临界区前必须获得锁,离开临界区后必须释放锁。

1.3 互斥机制(Mutual Exclusion)

通过互斥锁(Mutex)实现。线程在进入临界区前申请锁(加锁),如果锁已被其他线程持有,则当前线程会被阻塞;访问完临界资源后释放锁(解锁),允许其他线程获取。

2. 互斥锁的初始化与销毁

POSIX 线程库(pthread)提供了互斥锁的操作接口。使用前需包含头文件 <pthread.h>

2.1 动态初始化:pthread_mutex_init

c 复制代码
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr);

参数说明:

  • mutex:指向要初始化的互斥锁对象。
  • attr:互斥锁属性指针。通常传入 NULL 表示使用缺省属性(快速互斥锁)。

返回值:

  • 成功时返回 0
  • 失败时返回错误码(非零),可通过 strerror(errno) 查看错误信息。

示例:

c 复制代码
pthread_mutex_t mutex;
if (pthread_mutex_init(&mutex, NULL) != 0) {
    perror("pthread_mutex_init failed");
    exit(EXIT_FAILURE);
}

2.2 静态初始化:PTHREAD_MUTEX_INITIALIZER

对于全局或静态的互斥锁,可以使用宏进行静态初始化,无需调用 pthread_mutex_init

c 复制代码
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

2.3 销毁互斥锁:pthread_mutex_destroy

c 复制代码
int pthread_mutex_destroy(pthread_mutex_t *mutex);

注意事项:

  • 只能销毁已初始化且未被锁定的互斥锁。
  • 在 Linux 中,互斥锁本身不占用系统资源(如文件描述符),因此 pthread_mutex_destroy 的主要作用是检查锁状态(如果锁处于锁定状态,则返回 EBUSY)并使其不可用。
  • 销毁后的互斥锁不可再使用,除非重新初始化。

3. 互斥锁的基本使用

3.1 加锁:pthread_mutex_lock

c 复制代码
int pthread_mutex_lock(pthread_mutex_t *mutex);
  • 如果互斥锁未被锁定,则调用线程将其锁定并立即返回。
  • 如果互斥锁已被其他线程锁定,则调用线程将被阻塞,直到锁被释放。

3.2 解锁:pthread_mutex_unlock

c 复制代码
int pthread_mutex_unlock(pthread_mutex_t *mutex);
  • 释放由调用线程锁定的互斥锁。
  • 如果其他线程正在等待该锁,其中一个将被唤醒并获得锁。

3.3 尝试加锁:pthread_mutex_trylock

c 复制代码
int pthread_mutex_trylock(pthread_mutex_t *mutex);
  • 尝试锁定互斥锁。
  • 如果锁可用,则锁定并返回 0
  • 如果锁已被其他线程锁定,则立即返回错误码 EBUSY,而不会阻塞调用线程。

4. 完整使用示例

下面是一个简单的多线程计数器示例,演示如何使用互斥锁保护共享变量。

c 复制代码
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

#define THREAD_NUM 5
#define COUNT_LIMIT 10000

int counter = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

void* increment(void* arg) {
    for (int i = 0; i < COUNT_LIMIT; ++i) {
        pthread_mutex_lock(&mutex); // 进入临界区前加锁
        counter++;
        pthread_mutex_unlock(&mutex); // 离开临界区后解锁
    }
    return NULL;
}

int main() {
    pthread_t threads[THREAD_NUM];

    // 创建多个线程同时增加计数器
    for (int i = 0; i < THREAD_NUM; ++i) {
        pthread_create(&threads[i], NULL, increment, NULL);
    }

    // 等待所有线程结束
    for (int i = 0; i < THREAD_NUM; ++i) {
        pthread_join(threads[i], NULL);
    }

    printf("Final counter value: %d (expected: %d)\n", counter, THREAD_NUM * COUNT_LIMIT);
    return 0;
}

输出结果:

复制代码
Final counter value: 50000 (expected: 50000)

如果没有互斥锁保护,多个线程同时执行 counter++(该操作非原子)会导致最终结果小于预期。

相关推荐
麦兜和小可的舅舅1 小时前
从原理到实战:Linux 系统性能诊断核心指标全解析及生产系统故障分析复盘
大数据·linux·运维
滴滴滴嘟嘟嘟.1 小时前
强化学习-PPO 奖励塑形实验:Pendulum-v1 中角速度惩罚权重的影响
开发语言·python
灵晔君2 小时前
【C++】标准模板库STL——set /multiset/map /multimap
开发语言·c++
从此以后自律2 小时前
Java 并发编程完整讲解
java·开发语言
Sylvia33.2 小时前
足球数据接口开发实战:如何用火星数据API盘活赛事应用
java·服务器·开发语言·数据库·python
mounter6252 小时前
质检员与超能引擎的碰撞:KASAN 护航 eBPF JIT 的技术演进与安全抉择
linux·ebpf·linux kernel·kernel·kasan
z落落2 小时前
C# WinForm 自定义控件
开发语言·c#
风123456789~2 小时前
【Linux专栏】history常用命令
linux·运维·服务器
熬夜苦读学习2 小时前
基于websocket的多用户五子棋网页游戏
linux·服务器·网络·websocket·网络协议·游戏·五子棋