服务器测评网
我们一直在努力

Linux多线程编程中,互斥锁究竟该如何正确使用?

在多线程编程环境中,当多个线程需要访问和操作共享资源(如全局变量、共享内存等)时,就可能发生“竞态条件”,为了避免数据混乱和不一致,Linux/POSIX 提供了互斥锁作为线程同步的重要工具,互斥锁本质上是一种“互斥”机制,它确保在任意时刻,只有一个线程能够进入被保护的代码区域,通常称为“临界区”。

Linux多线程编程中,互斥锁究竟该如何正确使用?

互斥锁的生命周期

使用一个互斥锁通常遵循固定的生命周期:创建与初始化、加锁、访问共享资源、解锁,最后销毁。

  1. 初始化:在使用前,必须先初始化一个互斥锁,可以静态初始化,也可以使用 pthread_mutex_init 函数动态初始化。
  2. 加锁:线程在进入临界区之前,尝试获取互斥锁的所有权,如果锁未被占用,则该线程获得锁并继续执行;如果锁已被其他线程持有,则该线程会阻塞,直到锁被释放。
  3. 解锁:当线程离开临界区时,必须释放互斥锁,以便其他等待的线程可以获取它。
  4. 销毁:当互斥锁不再需要时,应销毁它以释放系统资源。

核心函数详解

POSIX 线程库(pthread)提供了一系列函数来操作互斥锁,下表总结了最核心的几个函数:

Linux多线程编程中,互斥锁究竟该如何正确使用?

函数 功能 参数 备注
pthread_mutex_init 初始化一个互斥锁 pthread_mutex_t *mutex, const pthread_mutexattr_t *attr attr 通常设为 NULL,表示使用默认属性,成功返回0。
pthread_mutex_lock 阻塞式加锁 pthread_mutex_t *mutex 如果锁已被占用,调用线程将一直阻塞,直到获得锁。
pthread_mutex_trylock 非阻塞式加锁 pthread_mutex_t *mutex 如果锁可用,则获取;否则立即返回错误码 EBUSY,不会阻塞。
pthread_mutex_unlock 解锁 pthread_mutex_t *mutex 释放锁,允许其他等待的线程获取,必须由持有锁的线程调用。
pthread_mutex_destroy 销毁互斥锁 pthread_mutex_t *mutex 释放互斥锁占用的资源,销毁后不可再使用。

示例代码

下面的例子演示了如何使用互斥锁保护一个共享的计数器,若无保护,最终结果很可能小于预期值;加上互斥锁后,结果将完全正确。

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#define NUM_THREADS 2
#define ITERATIONS 1000000
int shared_counter = 0;
pthread_mutex_t counter_mutex;
void* increment_counter(void* arg) {
    for (int i = 0; i < ITERATIONS; ++i) {
        // 加锁,保护临界区
        pthread_mutex_lock(&counter_mutex);
        // --- 临界区开始 ---
        shared_counter++;
        // --- 临界区结束 ---
        // 解锁
        pthread_mutex_unlock(&counter_mutex);
    }
    return NULL;
}
int main() {
    pthread_t threads[NUM_THREADS];
    // 初始化互斥锁
    if (pthread_mutex_init(&counter_mutex, NULL) != 0) {
        perror("Failed to initialize mutex");
        return 1;
    }
    // 创建线程
    for (int i = 0; i < NUM_THREADS; ++i) {
        if (pthread_create(&threads[i], NULL, increment_counter, NULL) != 0) {
            perror("Failed to create thread");
            return 1;
        }
    }
    // 等待所有线程完成
    for (int i = 0; i < NUM_THREADS; ++i) {
        pthread_join(threads[i], NULL);
    }
    printf("Expected counter value: %d\n", NUM_THREADS * ITERATIONS);
    printf("Actual counter value: %d\n", shared_counter);
    // 销毁互斥锁
    pthread_mutex_destroy(&counter_mutex);
    return 0;
}

注意事项与最佳实践

  • 避免死锁:死锁是最常见的错误,例如一个线程获取了锁后忘记释放,或者在持有锁的过程中调用了可能导致阻塞的函数,务必确保 lockunlock 成对出现,即使在 if 或错误处理路径中也要如此。
  • 减小锁的粒度:临界区应尽可能短小,只在确实需要保护共享资源时才持有锁,长时间占用锁会严重降低程序的并发性能。
  • 选择合适的锁类型:通过 pthread_mutexattr_t 可以设置互斥锁的属性,例如设置成检错锁(PTHREAD_MUTEX_ERRORCHECK)或可重入锁(PTHREAD_MUTEX_RECURSIVE),以适应不同的场景需求。

互斥锁是构建线程安全、稳定可靠的Linux应用程序的基石,正确理解并熟练使用它,是每一位多线程开发者的必备技能。

Linux多线程编程中,互斥锁究竟该如何正确使用?

赞(0)
未经允许不得转载:好主机测评网 » Linux多线程编程中,互斥锁究竟该如何正确使用?