😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀
🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭
⏰发布时间⏰:
本文未经允许,不得转发!!!
目录
- 🎄一、概述
- 🎄二、为什么需要互斥量
- 🎄三、互斥量的使用
- ✨3.1 互斥量的初始化
- ✨3.2 互斥量的加锁和解锁
- ✨3.3 互斥量的销毁
- 🎄四
- 🎄五
- 🎄六、
🎄一、概述
互斥量采用的是英文mutual exclusive(互相排斥之意)的缩写,即mutex
,是多线程编程中,常用来进行同步访问的方式之一。根据互斥量的用法,可以形象地将互斥量比喻成一把锁,锁住关键代码(临界区),每次只允许一个线程进入。
互斥量的工作机制:互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所以在该互斥锁上的阻塞线程都会变成可进行状态,第一个变成运行状态的线程可以对互斥量加锁,其他线程在次被阻塞,等待下次运行状态。
🎄二、为什么需要互斥量
大部分情况下, 线程使用的数据都是局部变量, 变量的地址在线程栈空间内, 这种情况下, 变量
归属于单个线程, 其他线程无法获取到这种变量。但多数的多线程编程种,会出现一些资源是多个线程共享使用的,如:全局变量、堆空间指针变量等。
当多个线程可以同时改变某个共享资源,并且这个改变的操作不是原子操作,而又不加限制的话,那么改变的结果可能是意想不到的。这就是需要互斥量的原因。
🌰看例子:下面例子,创建4个线程,对共享资源(g_Count
全局变量)执行了1000万次自加1操作。我们期待的结果应该是4000万,但运行结果有时却非4000万。
// 08_mutex_test.c
// gcc 08_mutex_test.c -lpthread
#include <stdio.h>
#include <pthread.h>
#include <sys/syscall.h>
int g_Count = 0;
void *func(void *arg)
{int i=0;for(i=0; i<10000000; i++){g_Count++;}return NULL;
}int main()
{// 创建4个线程pthread_t threadId[4];int i=0;for(i=0; i<4; i++){pthread_create(&threadId[i], NULL, func, NULL);}for(i=0; i<4; i++){pthread_join(threadId[i],NULL);printf("join threadId=%lx\n",threadId[i]);}printf("g_Count=%d\n",g_Count);return 0;
}
运行结果如下:执行三次,只出现了一次4000万,且每次结果都不一样。因为g_Count++;
语句不是一个原子操作,假设4个线程同时获取到g_Count
时值为1,4个线程都执行g_Count++
后,每个线程都认为此时g_Count
的值为2,但4个线程执行了4次了。
综上所述,当多个线程可以同时操作共享资源时,需要满足下面三点来使各个线程互斥:
1、当一个线程操作共享资源时,不允许其他线程同时操作该资源。
2、当线程不再操作共享资源时,不能阻碍其他线程操作该资源。
3、当多个线程同时操作一个共享资源时,只允许一个线程执行操作。
🎄三、互斥量的使用
正确地使用互斥量来保护共享数据,首先要定义和初始化互斥量。然后是使用互斥量的加锁、释放锁来保护共享数据,最后使用完销毁互斥量。
✨3.1 互斥量的初始化
POSIX提供了两种初始化互斥量的方法。
-
1、是将
PTHREAD_MUTEX_INITIALIZER
赋值给定义的互斥量,如下:#include <pthread.h> pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
但这个方法没办法设置互斥量的属性,也不适用于动态分配的互斥量,比较少用。
-
2、使用
pthread_mutex_init
初始化互斥量。如下:#include <pthread.h> int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);
第二个
pthread_mutexattr_t
指针的入参,是用来设定互斥量的属性的。大部分情况下,并不需要设置互斥量的属性,传递NULL即可,表示使用互斥量的默认属性。
调用pthread_mutex_init
之后, 互斥量处于没有加锁的状态。
✨3.2 互斥量的加锁和解锁
关于互斥量的加锁和解锁,POSIX提供了如下接口:
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
✨3.3 互斥量的销毁
在确定不再需要互斥量的时候, 就要销毁它。 在销毁之前, 有三点需要注意:
1、使用PTHREAD_MUTEX_INITIALIZER
初始化的互斥量无须销毁。
2、不要销毁一个已加锁的互斥量, 或者是真正配合条件变量使用的互斥量。
3、已经销毁的互斥量, 要确保后面不会有线程再尝试加锁。
销毁互斥量的接口如下:
#include <pthread.h>
int pthread_mutex_destroy(pthread_mutex_t *mutex);
当互斥量处于已加锁的状态, 或者正在和条件变量配合使用, 调用pthread_mutex_destroy函数会返回EBUSY
错误码。
🎄四
🎄五
🎄六、
如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁