【Linux C

您所在的位置:网站首页 多线程同步和互斥有何异同 【Linux C

【Linux C

2024-06-10 01:53| 来源: 网络整理| 查看: 265

😁博客主页😁:🚀https://blog.csdn.net/wkd_007🚀 🤑博客内容🤑:🍭嵌入式开发、Linux、C语言、C++、数据结构、音视频🍭 ⏰发布时间⏰:

本文未经允许,不得转发!!!

目录 🎄一、概述🎄二、为什么需要互斥量🎄三、互斥量的使用✨3.1 互斥量的初始化✨3.2 互斥量的销毁✨3.3 互斥量的加锁和解锁 🎄四、互斥量的属性🎄五、总结

相关文章: 【Linux C | 多线程编程】线程同步 | 互斥量(互斥锁)介绍和使用 【Linux C | 多线程编程】线程同步 | 条件变量(万字详解) 【Linux C | 多线程编程】线程同步 | 条件变量 的 使用总结 【Linux C | 多线程编程】线程同步 | 信号量(无名信号量) 及其使用例子 【Linux C | 多线程编程】互斥量、信号量、条件变量对比总结

在这里插入图片描述

🎄一、概述

互斥量采用的是英文mutual exclusive(互相排斥之意)的缩写,即mutex,是多线程编程中,常用来进行同步访问的方式之一。根据互斥量的用法,可以形象地将互斥量比喻成一把锁,锁住关键代码(临界区),每次只允许一个线程进入。

互斥量的工作机制:互斥量从本质上说是一把锁,在访问共享资源前对互斥量进行加锁,在访问完成后释放互斥量上的锁。对互斥量进行加锁以后,任何其他试图再次对互斥量加锁的线程将会被阻塞直到当前线程释放该互斥锁。如果释放互斥锁时有多个线程阻塞,所以在该互斥锁上的阻塞线程都会变成可进行状态,第一个变成运行状态的线程可以对互斥量加锁,其他线程在次被阻塞,等待下次运行状态。

在这里插入图片描述

🎄二、为什么需要互斥量

大部分情况下, 线程使用的数据都是局部变量, 变量的地址在线程栈空间内, 这种情况下, 变量归属于单个线程, 其他线程无法获取到这种变量。但多数的多线程编程种,会出现一些资源是多个线程共享使用的,如:全局变量、堆空间指针变量等。

当多个线程可以同时改变某个共享资源,并且这个改变的操作不是原子操作,而又不加限制的话,那么改变的结果可能是意想不到的。这就是需要互斥量的原因。

🌰看例子:下面例子,创建4个线程,对共享资源(g_Count全局变量)执行了1000万次自加1操作。我们期待的结果应该是4000万,但运行结果有时却非4000万。

// 08_mutex_test.c // gcc 08_mutex_test.c -lpthread #include #include #include int g_Count = 0; void *func(void *arg) { int i=0; for(i=0; i // 创建4个线程 pthread_t threadId[4]; int i=0; for(i=0; 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_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

但这个方法没办法设置互斥量的属性,也不适用于动态分配的互斥量,比较少用。

2、使用 pthread_mutex_init 初始化互斥量。如下:

#include int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);

第二个pthread_mutexattr_t指针的入参,是用来设定互斥量的属性的。大部分情况下,并不需要设置互斥量的属性,传递NULL即可,表示使用互斥量的默认属性。 调用pthread_mutex_init之后, 互斥量处于没有加锁的状态。

✨3.2 互斥量的销毁

使用pthread_mutex_init初始化的互斥量,在确定不再需要互斥量的时候, 就要销毁它。 在销毁之前, 有三点需要注意: 1、使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量无须销毁。 2、不要销毁一个已加锁的互斥量, 或者是真正配合条件变量使用的互斥量。 3、已经销毁的互斥量, 要确保后面不会有线程再尝试加锁。

销毁互斥量的接口如下:

#include int pthread_mutex_destroy(pthread_mutex_t *mutex);

当互斥量处于已加锁的状态, 或者正在和条件变量配合使用, 调用pthread_mutex_destroy函数会返回EBUSY错误码。

✨3.3 互斥量的加锁和解锁

关于互斥量的加锁和解锁,POSIX提供了如下接口:

#include int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); int pthread_mutex_timedlock (pthread_mutex_t * mutex, const struct timespec *abstime); int pthread_mutex_unlock(pthread_mutex_t *mutex); pthread_mutex_lock:对互斥量加锁,如果互斥量未锁定,则将互斥量锁定, 同时返回成功。如果互斥量已经上锁,则一直阻塞等待互斥量解锁;pthread_mutex_trylock:对互斥量加锁,如果互斥量未锁定,则将互斥量锁定, 同时返回成功。如果互斥量已经上锁,则不会阻塞等待,直接返回EBUSY;pthread_mutex_timedlock:对互斥量加锁,如果互斥量未锁定,则将互斥量锁定, 同时返回成功。如果互斥量已经上锁,则等待abstime设置的时间,如果还处于锁定状态,直接返回ETIMEOUT;注意,abstime是绝对时间,如果最多等待2分钟, 那么这个值应该是当前时间加上2分钟pthread_mutex_unlock:对互斥量解锁。

在这里插入图片描述

🎄四、互斥量的属性

线程和线程的同步对象(互斥量,读写锁,条件变量)都具有属性。在修改属性前都需要对该结构进行初始化。使用后要把该结构回收。大部分情况使用的都是默认属性。

互斥量的属性相关接口:

int pthread_mutexattr_init (pthread_mutexattr_t *attr); // 初始化互斥量属性为默认属性 int pthread_mutexattr_destroy (pthread_mutexattr_t *attr);// 销毁互斥量属性 /* Get the process-shared flag of the mutex attribute ATTR. */ int pthread_mutexattr_getpshared (const pthread_mutexattr_t * attr,int *pshared); /* Set the process-shared flag of the mutex attribute ATTR. */ int pthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int shared)

attr 中 pshared 属性表示用这个属性对象创建的互斥锁的作用域,它的取值可以是 PTHREAD_PROCESS_PRIVATE或PTHREAD_PROCESS_SHARED。

PTHREAD_PROCESS_PRIVATE:默认属性,只有和创建这个互斥锁的线程在同一个进程中的线程才能访问这个互斥锁;PTHREAD_PROCESS_SHARED:所创建的互斥锁将被保存在共享内存中,可以被多个进程中的线程共享。

互斥锁类型:

PTHREAD_MUTEX_NORMAL;PTHREAD_MUTEX_ERRORCHECK;PTHREAD_MUTEX_RECURSIVE;PTHREAD_MUTEX_DEFAULT。

在这里插入图片描述

🎄五、总结

本文介绍了Linux系统下,多线程编程常用的互斥量,先是介绍了需要互斥量的原因,然后介绍互斥量的使用,并给出使用例子。

下面是使用互斥量对第二小节例子进行修改后的代码:

// 08_mutex_test.c // gcc 08_mutex_test.c -lpthread #include #include #include int g_Count = 0; pthread_mutex_t g_mutex; void *func(void *arg) { int i=0; for(i=0; i pthread_mutex_init(&g_mutex, NULL); // 创建4个线程 pthread_t threadId[4]; int i=0; for(i=0; i pthread_join(threadId[i],NULL); printf("join threadId=%lx\n",threadId[i]); } printf("g_Count=%d\n",g_Count); pthread_mutex_destroy(&g_mutex); return 0; }

在这里插入图片描述 如果文章有帮助的话,点赞👍、收藏⭐,支持一波,谢谢 😁😁😁

参考: 《linux_多线程》 《Linux环境编程:从应用到内核》



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3