Linux线程编程(3)【互斥锁】
一、什么是互斥锁
互斥锁(Mutex,全称Mutual Exclusion)是一种并发编程中常用的同步机制,用于保护共享资源的访问,以避免多个线程同时访问共享资源而导致的数据竞争和不确定的结果。
互斥锁的主要目的是确保在任意时刻只有一个线程可以获取锁,并进入临界区(修改共享资源)。其他线程在尝试获取该锁时会被阻塞,直到持有锁的线程释放锁为止。这样,通过对共享资源的访问进行串行化,就可以避免并发访问带来的问题。
互斥锁通常有两个状态:锁定(locked)和未锁定(unlocked)。当一个线程获得互斥锁后,将使其状态为锁定,其他线程再次尝试获取锁时会被阻塞。当持有锁的线程完成对共享资源的操作后,会将锁状态设为未锁定,其他线程可以竞争获取锁。
互斥锁是一种重要的线程同步机制,可用于确保线程安全,防止竞态条件,以及在多线程环境下正确地处理共享资源。然而,过多或不恰当地使用互斥锁也可能导致性能下降和死锁等问题,因此在使用时需要谨慎考虑,并根据具体情况选择合适的同步机制。
二、使用的函数
-
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr)
- 函数作用:初始化互斥锁。
- 参数:
mutex
:指向互斥锁变量的指针。在函数中,互斥锁将被初始化。attr
:指向互斥锁属性的指针,通常为NULL
,表示使用默认属性。
-
int pthread_mutex_destroy(pthread_mutex_t *mutex)
- 函数作用:销毁互斥锁。
- 参数:
mutex
:指向互斥锁变量的指针。在函数中,互斥锁将被销毁。
-
int pthread_mutex_lock(pthread_mutex_t *mutex)
- 函数作用:对互斥锁上锁(获取锁)。
- 参数:
mutex
:指向互斥锁变量的指针。调用该函数时,如果互斥锁已被其他线程锁定,则调用线程将阻塞,直到互斥锁被解锁。
-
int pthread_mutex_unlock(pthread_mutex_t *mutex)
- 函数作用:解锁互斥锁。
- 参数:
mutex
:指向互斥锁变量的指针。调用该函数时,如果互斥锁当前没有被锁定,则函数没有任何效果。如果互斥锁被锁定,调用线程将释放该锁。
三、实现互斥锁
1.动态初始化
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
//int pthread_join(pthread_t thread, void **retval);
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
//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_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);//dongtai
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int share_resource = 0;
pthread_mutex_t mutex;
void *thread1_function()
{
for(int i = 0; i<5; i++)
{
pthread_mutex_lock(&mutex);//上锁
share_resource++;
printf("thread1 : %d\n", share_resource);
pthread_mutex_unlock(&mutex);//解锁
}
return NULL;
}
void *thread2_function()
{
for(int i = 0; i<5; i++)
{
pthread_mutex_lock(&mutex);
share_resource--;
printf("thread2 : %d\n", share_resource);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main()
{
pthread_t thread1,thread2;
if(pthread_mutex_init(&mutex,NULL) != 0)//动态初始化
{
perror("init");
exit(-1);
}
pthread_create(&thread1, NULL, thread1_function, NULL);//创建线程
pthread_create(&thread2, NULL, thread2_function, NULL);
pthread_join(thread1, NULL);//等待线程执行完
pthread_join(thread2, NULL);
pthread_mutex_destroy(&mutex);//销毁锁
return 0;
}
2.静态初始化
代码如下(示例):
#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <stdlib.h>
//int pthread_join(pthread_t thread, void **retval);
//int pthread_create(pthread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);
//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_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);//dongtai
//pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int share_resource = 0;
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;//静态初始化
void *thread1_function()
{
for(int i = 0; i<5; i++)
{
pthread_mutex_lock(&mutex);
share_resource++;
printf("thread1 : %d\n", share_resource);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
void *thread2_function()
{
for(int i = 0; i<5; i++)
{
pthread_mutex_lock(&mutex);
share_resource--;
printf("thread2 : %d\n", share_resource);
pthread_mutex_unlock(&mutex);
}
return NULL;
}
int main()
{
pthread_t thread1,thread2;
pthread_create(&thread1, NULL, thread1_function, NULL);
pthread_create(&thread2, NULL, thread2_function, NULL);
pthread_join(thread1, NULL);
pthread_join(thread2, NULL);
pthread_mutex_destroy(&mutex);
return 0;
}
注意
如果你只是在某一段代码中使用了互斥锁(mutex),并且没有在其他地方尝试锁定同一个互斥锁,那么只有这段代码中涉及锁操作的部分可能会阻塞。
具体来说:
-
当你的线程在这个地方执行到
pthread_mutex_lock(&mutex);
并成功加锁后,此线程会继续执行下面的代码,而不会阻塞。 -
如果在这段代码执行期间,另一个线程也尝试通过
pthread_mutex_lock(&mutex);
对同一个mutex进行加锁,那么这个线程会被阻塞,直到第一个线程调用pthread_mutex_unlock(&mutex);
释放锁。 -
其他不涉及这个mutex的代码不会因为这个锁而阻塞。
所以,如果只有这一段代码使用了该互斥锁,且没有其他地方试图锁定这个mutex,那么只有在这个代码段内部涉及锁操作的部分会受到锁的影响,其他代码不会被阻塞。
如果说我在这个互斥所里面更改了一个全局变量,那么我其他线程仍能够获得这个变量的值。
因为互斥锁(mutex)的主要目的是为了保证在并发环境下对共享资源的独占访问,以避免数据不一致或者冲突的情况。但它并不会阻止其他线程访问被保护资源的当前值。
假设你在一个线程里使用互斥锁保护了一个全局变量的写操作,这意味着当这个线程持有锁并修改该全局变量时,其他线程不能同时修改这个全局变量。但其他线程仍然可以读取该变量的值,只是读到的可能是正在被修改的值。
为了确保数据的一致性和完整性,通常建议在所有访问共享资源(无论是读还是写)的地方都使用互斥锁,这样可以确保在读取时数据是稳定的,而不是处于一个中间、不一致的状态。
简而言之,其他线程可以读取在互斥锁中更改的全局变量的值,但为了数据一致性,你可能也需要对读操作使用相同的互斥锁。