空间做子网站,网址导航可以卸载吗,网页版游戏哪个好玩,wordpress文章折叠引言#xff1a;
北京时间#xff1a;2023/7/19/15:22#xff0c;昨天更新完博客#xff0c;和舍友下了一会棋#xff0c;快乐就是这么简单#xff0c;哈哈哈#xff01;总体来说#xff0c;摆烂程度得到一定的改善#xff0c;想要达到以前的水准#xff0c;需要一定…引言
北京时间2023/7/19/15:22昨天更新完博客和舍友下了一会棋快乐就是这么简单哈哈哈总体来说摆烂程度得到一定的改善想要达到以前的水准需要一定的契机毕竟人生在世快乐最重要是吧更文带给我的快乐已经没有那么多了虽然欠了非常多的作业非常多的课需要补很多的题等着我去刷怎叹一个懒字了得本质还是作息控制不住哎这周小目标更文4篇只要能达到这个水准其它的都好说想到还有那么多课没有看现在真的挺头疼不管那么多正式进入该篇博客的正题承接上篇博客有关多线程互斥和同步相关的知识该篇博客我们继续深入理解一下有关线程的互斥和同步吧 深入线程互斥
承接上篇博客有关线程互斥相关知识此时我们在深入理解一下线程互斥。在上篇博客中我们重点强调了为什么要进行线程互斥和如何进行线程互斥也就是如何让一份共享资源变为临界资源每次访问共享资源时只能有一个线程获得资源的使用权加锁其他线程必须等待直到该线程释放资源后才能继续执行解锁。并且在此基础上我们还简单介绍了有关线程互斥的相关线程库接口如pthread_mutex_init,pthread_mutex_lock,pthread_mutex_unlock,pthread_mutex_destroy 当然我们也明白在使用这些线程互斥接口的前提是我们定义了一个全局的锁pthread_mutex_t mutex; 在使用文档中当我们定义了一个全局的锁结构时该锁结构是一定需要进行初始化和销毁也就是必须使用pthread_mutex_init接口和pthread_mutex_destroy但是使用文档中也给我们提供了另一种方法让我们可以不需要使用这两个接口就能完成锁结构的初始化和销毁在定义锁结构时直接在其后面添加对应的宏结构pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;这样就可以直接在定义锁的同时完成对锁的初始化和最终锁的销毁更简便的供给我们使用。那么此时有的同学就会有问题了有了这个宏定义还需要之前初始化和销毁的接口干嘛呢答案是使用宏定义快速对锁进行初始化的前提是该锁是一个全局变量的锁只有是全局变量的锁才有资格使用pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;方法因为如果对应的锁是局部变量那么就会导致当函数调用完之后对应的局部变量锁随着栈帧的销毁而销毁最终导致编译器无法根据地址再找到对应的锁结构造成资源泄露问题所以当定义一个局部变量的锁时此时就只能使用pthread_mutex_init接口和pthread_mutex_destroy接口进行对应的初始化和销毁。当然此时还没有讲清楚为什么局部变量不能用 PTHREAD_MUTEX_INITIALIZER方法只有全局变量可以这是因为如果使用了 PTHREAD_MUTEX_INITIALIZER方法本质是将对应的锁变量初始化为默认值并且将其存储在静态区中使其生命周期与该进程的声明周期相同当进程结束操作系统就会自动回收该锁变量占用的资源达到销毁目的所以这也就是为什么局部变量锁不能使用 PTHREAD_MUTEX_INITIALIZER方法的原因。
互斥锁细节介绍 搞定了上述知识此时我们对锁就有了进一步的理解当然想要彻底搞定锁相关的知识重点是搞定锁的实现原理明白它为什么可以让共享资源变成临界资源不过在了解锁实现原理之前此时我们先来谈谈有关加锁方面的细节知识如一个共享资源只允许使用一把锁进行保护不然就有可能导致死锁问题。并且在进行加锁时加锁的位置一定要合理尽量细化只要将共享资源保护起来就行不允许大范围加锁否则会导致代码执行效率非常低。然后还要明白对于锁来说加锁和解锁本身就是一个原子结构也就是因为锁也属于共享资源如果不对锁进行保护的话那么同理会导致竞态条件问题所以锁的设计者在设计锁的时候已经将锁设计为原子结构也就是当一个线程在访问一个锁的时候别的线程不允许访问该锁。最后还要明白一个临界区不仅仅只是一行代码也可能是一批代码所以当某个线程在执行该临界区中的代码时该线程有可能会因为时间片到了而被操作系统调度使得对应临界区中的代码没有执行完但是这并不会导致其它线程可以访问对应的临界资源因为加锁之后无论临界区的代码是否执行完毕其它线程都无法访问到对应的临界资源这也正是加锁之后带来的线程串行化表现。
锁的基本实现原理 搞定了上述知识我们正式进入锁的实现原理讲解首先明白锁本质就是一个互斥量因为其可以保护共享资源也就是只让一个线程访问对应的资源所以我们将这种特性互斥称之为锁也叫互斥锁。明白了这点之后接下来我们要搞定的也就是互斥量如何进行互斥从而让多个线程访问共享资源时只有一个线程能够成功访问如下图所示 如上图所示此时我们明白对于所有线程来说它们在同时访问同一份共享资源时因为我们进行了加锁操作pthread_mutex_lock所以它们在访问该共享资源之前就需要执行加锁接口相关的代码也就是如上图所示的伪代码首先它们要将自己上下文中的%al变量初始化为0然后再使用exchange接口将我们事先定义并且初始化好的锁变量mutex从内存中交换到寄存器也就是交换到自己的上下文中让%al由0变1让mutex由1变0完成了这一步骤之后对应的线程就实现了互斥操作也就是我们所说的加锁并且此时mutex就是该互斥操作中的互斥量。交换的目的就是让这个互斥量只被一个线程拿到当下一个线程也要交换时由于mutex互斥量已经变为了锁定状态0此时它就无法获取到mutex中的1未锁定状态从而无法执行pthread_mutex_lock中的后序代码只能被操作系统挂起等待if语句判断。最后明白一点也就是我们一直说的锁是共享资源却不会造成竞态条件的原因是因为其设计成了原子性从上图我们就能看出一个线程在执行对应加锁代码时其中获取互斥量的过程仅仅就只是一个交换语句所以可以明白对于线程来说单独一句代码要么执行要么就是不执行所以对于加锁操作它天生就是原子性的。
所以同理解锁操作就是将mutex的值由0变1这样下一个线程在执行加锁操作时就可以获取到对应mutex互斥量中的1因为此时mutex处于未锁定状态1同理获取到之后交换mutex就又会处于锁定状态0所以这也是为什么有加锁操作就一定要有解锁操作否则就会造成死锁无论是那个线程都无法访问到该共享资源。
线程封装
明白了上述有关互斥锁的相关知识之后此时我们进行线程的封装也就是对pthread.h头文件中有关线程控制相关接口的封装实现一个自己的简易线程库当然无论是在C还是Java中它们的线程库都是和我们一样对pthread.h头文件进行的封装只不过在设计上不同所以导致不同的语言在线程库的使用上不同本质原因就是封装的方法不同如下代码所示就是我们自己对线程库的一个封装 如上图所示此时我们就使用Thread类完成了对线程库的一个简易封装重点就是注意参数类型和函数指针传参方面的问题并且还要注意有关静态成员函数相关的知识也就是如果在一个类中你因为参数的原因无法使用this指针那么此时你就可以使用静态成员函数使用static声明这样就完成了该成员函数和类之间的解耦但是因为你将该成员函数和类解耦所以也就导致该成员函数没有this指针最终导致该成员函数无法访问到类中的成员变量具体如何解耦这里我们不详谈这里注意明白会用就行。
互斥锁的封装
明白了上述有关线程库的封装此时我们再来看看有关互斥锁的封装当然此时的封装还是同理对系统接口进行封装而不是对伪代码进行封装本质就是为了让我们可以更方便的使用加锁和解锁如下代码所示 此时我们就完成了对锁的封装那么此时有的同学就会问了为什么要这样对锁进行封装呢如下代码所示本质就是为了让加锁和解锁操作变得更加简易 如上图所示通过两种不同的加锁和解锁操作我们发现如果将加锁和解锁操作封装在一个类的构造和析构函数中然后通过对该类对象进行传参这样可以非常方便的完成对共享资源的保护。
总结有关线程互斥互斥锁的基本原理和封装有关线程等相关知识我们就讲到这里啦更多有关线程互斥与同步的知识我们下篇博客见。