合肥手机网站开发,描述个人网站的建站过程,企业网站建设专业服务,八八网络科技有限公司引言#xff1a;
北京时间#xff1a;2023/7/29/16:34#xff0c;一切尽在不言中#xff0c;前几天追了几部电视剧#xff0c;看了几部电影#xff0c;刷了n个视屏#xff0c;在前天我们才终于从这快乐的日子里恢复过来#xff0c;然后看了两节课#xff0c;也就是上…引言
北京时间2023/7/29/16:34一切尽在不言中前几天追了几部电视剧看了几部电影刷了n个视屏在前天我们才终于从这快乐的日子里恢复过来然后看了两节课也就是上篇博客有关生产消费模型相关的知识从中我发现代码能力极具下降对于很多C语法感到非常陌生哈哈哈所以学习起来非常的痛苦不过在我不断的回顾和摸索中感觉自己得到了升华对许多语法的掌握好像又上了一层楼具体和以前比较掌握的怎样我也不清楚谁叫我们生而为人呢很多东西学的快忘的也快现在又不是复习的时候那么就只能在前进的道路上顺便复习一下哎不过好在现在有AI想要搞懂某个问题需要的成本还是非常低的也是因为有AI的存在我对我学过的知识可以说是搞懂的较为彻底我愿奉之为21世纪最伟大的发明当然我说的是国外的ChatGPT反之国内真的惨不忍睹。谈到这里我真的要吐糟一下通义千问xxxx用这东西说实话还不如百度当然文心一言由于没用过具体不好说但是估计也差不多。好了废话不多为了能够早点更文正式进入该篇博客的主题有关信号量相关的知识当然本质还是多线程相关知识。 回顾生产消费模型
在学习新知识之前我们先将上篇博客中有关生产消费模型的知识给回顾一下因为上篇博客由于时间原因生产消费模型讲解的并不够详细谁让生产消费模型目前对于我们来说非常重要呢当然对于以后的我们来说该知识也是非常重要的所以这块知识值的我们继续花费时间。
在上篇博客中我们重点把为什么有生产消费模型和生产消费模型的三种关系以及生产消费模型的优点给介绍了并且重点介绍了三种关系之间的关联互斥、同步因为这块知识涉及我们在使用代码自己实现该模型时的思路并且最终依据对应的知识将生产消费模型的代码自我实现了一份。所以此时我们知道对于生产消费模型来说首先它要有一个缓冲区也就是一个容器可以便于线程读取或者写入数据其次是要有两种不同的线程一种执行生产过程一种执行消费过程最后要让这两种不同的线程也就是生产者和消费者满足它们之间的三种关系生产者和生产者互斥、消费者和消费者互斥、生产者和消费者互斥且同步。注意 具体为什么生产者和消费者需要是同步且互斥的关系之前我们讲过从反例来看如果允许生产者和消费者同时进行也即是多线程之间一个线程读取一个线程写入那么此时就会导致当某个读取线程没有对应的数据可读时它就必须阻塞在哪里等待写入线程写入也就是违背了生产消费模型的初衷没有很好的实现不同线程之间的解耦。所以在生产消费模型中我们就一定需要让生产者和消费者保持同步状态也就是当缓冲区中有数据时才允许通知读取线程来读数据没数据时在写入线程写入的同时让读取线程可以去申请别的共享资源而不是阻塞。在此原理之上互斥关系随之产生。
从代码分析生产消费模型
对上述知识又有了一定理解之后此时我们就从代码来分析一下上述我们说的一个缓冲区两种线程三种关系。当然我们此时是以最简单的生产消费模型下手在缓冲区中我们并没有对不同区域进行同步而是对整体进行同步并且我们明白对于生产消费模型来说它不局限任何类型的数据所以对于存储数据的地方来说我们使用的是模板类型。
1.首先是一个缓冲区 同理上篇博客中所说此时我们的生产消费模型是基于BlockQueue队列作为缓冲区实现的具体代码如下图所示 2.其次是两种不同的线程 搞定了上述生产消费模型中缓冲区相关的知识其余知识就较为容易本质就是在利用该缓冲区进行数据的写入和读取而已所以此时我们就来看看生产者和消费者具体是如何对BlockQueue进行数据的读取和写入吧具体代码如下所示 当然由于我们在BlockQueue缓冲区中使用了互斥锁进行同步机制保护所以我们的程序并不在意访问共享资源缓冲区的线程数量并且明白因为上述consumer接口和productor接口除了BlockQueue提供的接口之外都是局部变量所以默认是可重入函数所以如果多个线程同时对其进行访问此时也不会引发线程安全问题单线程访问到多线程访问以及代码执行结果代码如下所示 最后一个知识点当然在上述代码中肯定还会存在其它七七八八的问题没有讲解但是身为优质博主责无旁贷只选那种非常不好理解的知识点进行重点讲解如此时对于多生产多消费过程在循环创建线程时这些线程具体是如何运行的这个知识点我没有记错的话应该在之前讲解pthread_create接口的使用方式上我们有进行过一定的理解现在我们再来好好理解一下重点在于明白单核和多核的区别我们都知道多线程之间是允许并行访问的但是这个并行对于单核来说并不是真实的并行因为每个线程的执行需要经过调度器的调度调度器再根据线程的优先级去优先调度优先级高的线程我们也把这个过程称为时间片轮转调度所以对于单核执行过程来说线程之间不是并行执行而是并发执行当然并发执行指的也就是多个线程在同一时间段内交替执行通过快速的切换来模拟同时执行的效果。 也就是说如果你是在单核条件下那么上述循环创建线程的执行过程就是依次将所有20线程创建完成操作系统完成只有当全部线程被创建完成之后调度器才会根据每个线程的优先级不同进行时间片轮转调度让它们执行相应的代码也就是我们上述的consumer/productor接口。明白了单核的线程循环创建过程之后此时多核同理多个单核也就是线程之间的执行是地地道道的并行执行不再是并发执行所以此时在循环创建线程时就有可能出现多个线程同时被创建此时就不需要等待其它线程创建完成因为多核并行完成此时就可以根据优先级被调度器直接调度执行了。
3.最后是两种不同线程之间的三种关系 明白了上述两种不同线程和单线程多线程的知识此时我们对生产消费模型中大部分的硬核知识以及误区都给重点强调了一遍来到这里我们可以说是无所不能并且由于生产者和生产者、消费者和消费者、生产者和消费者之间的三种关系我们已经在一个缓冲区中相关代码的实现中体现的淋漓尽致了所以这块知识没什么好讲的本质就是通过三种关系通过自己对缓冲区设计构建出一个在生产消费模型逻辑范围内的属于自己的生产消费模型具体玩的多花由你自己决定。
深入信号量相关知识
搞定了上篇博客有关生产消费模型的知识此时我们正式进入该篇博客的重点有关线程同步的最后一个知识点信号量在深入学习信号量之前我们要意识到在很久以前学习有关进程间通信相关知识时无论是在学习匿名管道、命名管道还是共享内存本质我们都是在让两个进程看到“同一份资源”而当我们谈到这同一份资源时我们很容易就能联想到目前学习有关线程的知识。所以同理当时我们在学习进程间通信了解信号量的本质就是为了让信号量去保护进程间看到的那一份共享资源只是当时我们没有线程的概念、没有同步与互斥的概念只知道想让两个进程看到同一份资源的前提是让它们先看到同一份信号量资源同理当时我们举过的购买电影票的实例对共享资源进行预定。而现在当我们有了线程、线程安全的概念有了并行访问、竞态条件的概念此时我们就知道共享资源是需要受到保护的否则就会引发线程安全问题此时我们对信号量的理解就是一个保护共享资源的同步机制。你想要访问某共享资源前提是你获取到了相应的信号量只有获取到了信号量才允许你进行下一步的操作否则阻塞。
为什么学习信号量 明白了上述有关知识之后也就明白了之前学习的信号量是什么和为什么以及现在学习到信号量是什么所以我们就有了很强的前后关联性从而让我们可以更好的深入相关知识。所以此时我们就来谈谈为什么还要深入信号量首先我们知道信号量是一个用于描述临界资源中资源数量的计数器它可以通过PV操作来实现对临界资源中可用资源数量的控制。同理上述所说当我们想要访问某临界资源时只有获取到了信号量资源才允许我们进行下一步操作通过这一特征我们不难发现信号量的使用和我们使用互斥锁是一样的只有当某个线程抢到了互斥锁之后该线程才有访问临界资源的能力那么我们为什么还需要学习信号量呢直接使用互斥锁来实现这一原理不就行了吗原因和我们之前在谈有关生产消费模型时同理因为对于一份共享资源我不一定需要设置互斥机制我可以让所有线程同时访问该共享资源来极大的增加吞吐量和提高线程并发效率同理超市不是只允许一个人逛所以此时我可以将共享资源通过加锁和条件变量反正就是用同步机制的方式分为一个一个不同的区域当所有线程在访问该共享资源时每一个区域中的数据因为被同步机制保护同时只允许一个线程访问所以在这样的控制方式下我们的程序不仅不会出现线程安全问题而且可以大幅度的提高该临界资源的吞吐量和线程的并发执行效率。所以为了实现这一目的此时就需要有一个计数器来记录当前共享资源中可用资源的数量也就是我们上述所说的信号量所以像互斥锁原理那样的信号量被我们称为二元信号量1-P-0-V-1反之称为多元信号量n-P-n-1-P/V-n-2/n。
注意 因为信号量也是共享资源同理需要通过原子性实现。并且明白当一个信号量被申请成功那么就表示共享资源中一定有某块临界资源已经预定给我使用了买票原理所以信号量的预定机制本质就是把判断某临界资源是否可用转换为申请信号量是否成功。 所以在以后的多线程环境编码中我们就可以直接使用信号量的方法来代替对临界资源是否可用的判断条件具体有待通过具体场景和代码分析。
信号量相关接口认识
在以前学习信号量的过程中我们也对信号量相关接口进行了一定的了解但是注意当时我们了解的是SystemV标准而今天我们学习的是POSIX标准无论是SystemV标准还是POSIX标准它们本质都只是操作系统中用于进程间通信和同步的两个标准接口这两个版本具体有什么区别可参考该篇博客SystemV和POSIX简介和对比 反正本质就是通信界大佬们在早些年定下的两个标准。
1.SystemV标准信号量 其中在以前学习SystemV标准时了解的接口有semget()、semctl()、semop()按照先后顺序它们的作用分别是创建信号量、删除信号量和对信号量加减。具体如何使用可回顾之前学习信号量时的博客之前的信号量博客
2.POSIX标准信号量 明白了上述知识之后我们来看看POSIX标准下的信号量接口此时可分为初始化信号量、销毁信号量和等待信号量具体使用形式如下所示
初始化信号量int sem_init(sem_t* sem,int pshared,unsigned int value); 第一个参数同理互斥锁和条件变量表示信号量参数的地址第二个参数是一个区分进程间通信和线程间通信的参数0表示线程非0表示进程第三个参数表示信号量的初始值也就是该共享资源中可用资源的数量。等待信号量int sem_wait(sem_t* sem); 该接口起到P操作也就是对信号量进行减减操作同理要等待信号量的地址。发布信号量int sem_post(sem_t* sem);同理该接口起到V的操作也就是对信号量进行加加的操作参数同理信号量的地址。销毁信号量int sem_destroy(sem_t* sem); 同理要销毁信号量的地址。
基于环形队列的CP问题
搞定了上述有关信号量的知识此时我们就需要进入实操阶段也就是在具体的场景中使用信号量接口来完成对一份共享资源中资源数量的控制本质也就是使用代码来维护我们自己的线程规则。当然因为时间原因所以这部分知识我们留到下篇博客再见把
总结有关信号量相关的知识本质就是想让我们见识更加高级的CP生产消费模型知识从而彻底的将多线程相关知识给搞定See you