Linux互斥锁

本文深入解析了多线程编程中互斥锁的概念及其操作方法,包括创建、初始化、获取、释放和销毁线程锁的过程。同时,介绍了阻塞、非阻塞和超时调用的互斥锁获取策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

互斥的概念

在多线程编程中,引入了对象互斥锁的概念,来保证共享数据操作的完整性。 每个对象都对应于一个可称为" 互斥锁" 的标记,这个标记用来保证在任一时刻, 只能有一个线程访问该对象。

互斥锁操作

互斥锁也可以叫线程锁,接下来说说互斥锁的的使用方法。

对互斥锁进行操作的函数,常用的有如下几个:

复制代码

#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex);
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abs_timeout);

复制代码

 

对线程锁进行操作的函数有很多,还包括许多线程锁属性的操作函数, 不过一般来说,对于并不复杂的情况, 只需要使用创建、获取锁、释放锁、删除锁这几个就足够了。

创建互斥锁

所以下面简单看一下如何创建和使用互斥锁。

在使用互斥锁之前,需要先创建一个互斥锁的对象。 互斥锁的类型是 pthread_mutex_t ,所以定义一个变量就是创建了一个互斥锁。

pthread_mutex_t mtx;

 

这就定义了一个互斥锁。但是如果想使用这个互斥锁还是不行的,我们还需要对这个互斥锁进行初始化, 使用函数 pthread_mutex_init() 对互斥锁进行初始化操作。

//第二个参数是 NULL 的话,互斥锁的属性会设置为默认属性
pthread_mutex_init(&mtx, NULL);

 

除了使用 pthread_mutex_init() 初始化一个互斥锁,我们还可以使用下面的方式定义一个互斥锁:

pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;

 

在头文件 /usr/include/pthread.h 中,对 PTHREAD_MUTEX_INITIALIZER 的声明如下

# define PTHREAD_MUTEX_INITIALIZER \
   { { 0, 0, 0, 0, 0, 0, { 0, 0 } } }

 

为什么可以这样初始化呢,因为互斥锁的类型 pthread_mutex_t 是一个联合体, 其声明在文件 /usr/include/bits/pthreadtypes.h 中,代码如下:

复制代码

/* Data structures for mutex handling.  The structure of the attribute
   type is not exposed on purpose.  */
typedef union
{
    struct __pthread_mutex_s
    {
        int __lock;
        unsigned int __count;
        int __owner;
#if __WORDSIZE == 64
        unsigned int __nusers;
#endif
        /* KIND must stay at this position in the structure to maintain
           binary compatibility.  */
        int __kind;
#if __WORDSIZE == 64
        int __spins;
        __pthread_list_t __list;
# define __PTHREAD_MUTEX_HAVE_PREV  1
#else
        unsigned int __nusers;
        __extension__ union
        {
            int __spins;
            __pthread_slist_t __list;
        };
#endif
    } __data;
    char __size[__SIZEOF_PTHREAD_MUTEX_T];
    long int __align;
} pthread_mutex_t;

复制代码

 

获取互斥锁

接下来是如何使用互斥锁进行互斥操作。在进行互斥操作的时候, 应该先"拿到锁"再执行需要互斥的操作,否则可能会导致多个线程都需要访问的数据结果不一致。 例如在一个线程在试图修改一个变量的时候,另一个线程也试图去修改这个变量, 那就很可能让后修改的这个线程把前面线程所做的修改覆盖了。

下面是获取锁的操作:

阻塞调用

pthread_mutex_lock(&mtx);

 

这个操作是阻塞调用的,也就是说,如果这个锁此时正在被其它线程占用, 那么 pthread_mutex_lock() 调用会进入到这个锁的排队队列中,并会进入阻塞状态, 直到拿到锁之后才会返回。

非阻塞调用

如果不想阻塞,而是想尝试获取一下,如果锁被占用咱就不用,如果没被占用那就用, 这该怎么实现呢?可以使用 pthread_mutex_trylock() 函数。 这个函数和 pthread_mutex_lock() 用法一样,只不过当请求的锁正在被占用的时候, 不会进入阻塞状态,而是立刻返回,并返回一个错误代码 EBUSY,意思是说, 有其它线程正在使用这个锁。

复制代码

int err = pthread_mutex_trylock(&mtx);
if(0 != err) {
    if(EBUSY == err) {
        //The mutex could not be acquired because it was already locked.
    }
}

复制代码

 

超时调用

如果不想不断的调用 pthread_mutex_trylock() 来测试互斥锁是否可用, 而是想阻塞调用,但是增加一个超时时间呢,那么可以使用 pthread_mutex_timedlock() 来解决, 其调用方式如下:

复制代码

struct timespec abs_timeout;
abs_timeout.tv_sec = time(NULL) + 1;
abs_timeout.tv_nsec = 0;

int err = pthread_mutex_timedlock(&mtx, &abs_timeout);
if(0 != err) {
    if(ETIMEDOUT == err) {
        //The mutex could not be locked before the specified timeout expired.
    }
}

复制代码

 

上面代码的意思是,阻塞等待线程锁,但是只等1秒钟,一秒钟后如果还没拿到锁的话, 那就返回,并返回一个错误代码 ETIMEDOUT,意思是超时了。

其中 timespec 定义在头文件 time.h 中,其定义如下

struct timespec
{
    __time_t tv_sec;        /* Seconds.  */
    long int tv_nsec;       /* Nanoseconds.  */
};

 

还需要注意的是,这个函数里面的时间,是绝对时间,所以这里用 time() 函数返回的时间增加了 1 秒。

释放互斥锁

用完了互斥锁,一定要记得释放,不然下一个想要获得这个锁的线程, 就只能去等着了,如果那个线程很不幸的使用了阻塞等待,那就悲催了。

释放互斥锁比较简单,使用 pthread_mutex_unlock() 即可:

pthread_mutex_unlock(&mtx);

 

销毁线程锁

通过 man pthread_mutex_destroy 命令可以看到 pthread_mutex_destroy() 函数的说明, 在使用此函数销毁一个线程锁后,线程锁的状态变为"未定义"。有的 pthread_mutex_destroy 实现方式,会使线程锁变为一个不可用的值。一个被销毁的线程锁可以被 pthread_mutex_init() 再次初始化。对被销毁的线程锁进行其它操作,其结果是未定义的。

对一个处于已初始化但未锁定状态的线程锁进行销毁是安全的。尽量避免对一个处于锁定状态的线程锁进行销毁操作。

销毁线程锁的操作如下:

pthread_mutex_destroy(&mtx)

 

同步地址:https://www.fengbohello.top/archives/linux-pthread-mutex

 


作  者:郝峰波 
个人网站:http://www.fengbohello.top/ 
E-mail : fengbohello@foxmail.com 
欢迎转载,转载请注明作者和出处。

### Linux 下的互斥锁实现与用法 在 Linux 环境下,互斥锁(mutex)主要用于保护共享资源免受多线程并发访问的影响。以下是关于互斥锁的具体实现和用法。 #### 1. 头文件引入 为了使用互斥锁功能,在程序中需要包含头文件 `<pthread.h>`[^3]。此头文件定义了 pthread_mutex_t 类型以及相关的操作函数。 #### 2. 定义互斥锁变量 互斥锁通过 `pthread_mutex_t` 数据结构表示。可以静态初始化或者动态初始化该数据结构: - 静态初始化: ```c pthread_mutex_t my_mutex = PTHREAD_MUTEX_INITIALIZER; ``` - 动态初始化: ```c pthread_mutex_t my_mutex; int result = pthread_mutex_init(&my_mutex, NULL); if (result != 0) { perror("Mutex initialization failed"); } ``` 上述代码中的第二个参数是一个指向属性对象的指针,通常设置为 `NULL` 表示默认属性。 #### 3. 加锁与解锁 加锁可以通过调用 `pthread_mutex_lock()` 或者尝试非阻塞加锁 `pthread_mutex_trylock()` 实现;而释放锁则通过 `pthread_mutex_unlock()` 函数完成。 - 加锁: ```c int lock_result = pthread_mutex_lock(&my_mutex); if (lock_result != 0) { perror("Failed to acquire mutex lock"); } ``` - 尝试加锁(非阻塞方式): ```c int trylock_result = pthread_mutex_trylock(&my_mutex); if (trylock_result == EBUSY) { printf("Mutex already locked by another thread\n"); } else if (trylock_result != 0) { perror("Error during trying to lock mutex"); } ``` - 解锁: ```c int unlock_result = pthread_mutex_unlock(&my_mutex); if (unlock_result != 0) { perror("Failed to release mutex lock"); } ``` 这些函数返回值为零时表示成功,非零值代表错误发生。 #### 4. 销毁互斥锁 当不再需要某个互斥锁时,应该销毁它以释放相关资源: ```c int destroy_result = pthread_mutex_destroy(&my_mutex); if (destroy_result != 0) { perror("Failed to destroy mutex"); } ``` 注意:只有未被任何线程锁定的情况下才能安全地销毁互斥锁。 #### 示例代码 下面给出一段完整的例子展示如何创建并使用互斥锁来同步两个线程之间的执行顺序。 ```c #include <stdio.h> #include <stdlib.h> #include <pthread.h> pthread_mutex_t count_mutex = PTHREAD_MUTEX_INITIALIZER; // 初始化全局互斥锁 int counter = 0; void* increase_counter(void *arg){ int i; for(i=0;i<100000;i++){ pthread_mutex_lock(&count_mutex); // 对临界区上锁 counter++; pthread_mutex_unlock(&count_mutex); // 解开对临界区的锁 } } int main(){ pthread_t t1,t2; /* 创建第一个线程 */ if(pthread_create(&t1,NULL,increase_counter,(void*)NULL)){ fprintf(stderr,"Error creating thread 1\n"); return EXIT_FAILURE; } /* 创建第二个线程 */ if(pthread_create(&t2,NULL,increase_counter,(void*)NULL)){ fprintf(stderr,"Error creating thread 2\n"); return EXIT_FAILURE; } /* 等待两个子线程结束 */ if(pthread_join(t1,NULL)) {fprintf(stderr,"Joining with thread 1 failed");} if(pthread_join(t2,NULL)) {fprintf(stderr,"Joining with thread 2 failed");} printf("Final value of 'counter'=%d.\n",counter); return EXIT_SUCCESS; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值