学习系统编程No.33【生产消费模型】

引言:

北京时间:2023/7/22/14:27,现实和预期往往相差是巨大的,哈哈哈!白天睡不醒,晚上睡不着,就像一个夜猫子一样。熬夜耍手机,我真的是专业的,已经连续好久没有正常睡过觉了。怀念学校里的日子,每天都有人激励我早睡和早起,此处@谢xx,不知他最近的学习状态如何,但我估计没有了我的存在,他应该也不是很积极,看到CSDN里的那600条私信,现在我就头痛,居然有人真的可以天天卷,不服气!收心,收心,今天或者明天能早睡,那我就成功了,本质也就是今天写完这篇博客之后,可以倒头就睡,不看电视,刷视屏,当然也就是早上可以起来码字, 那么咱就有希望实现日更,好了,废话不多说,正式进入该篇博客的主题,有关生产消费模型,当然还有线程互斥与同步相关知识。

在这里插入图片描述

线程安全问题和可重入问题

在学习该篇博客的主题,有关生产者消费者模型之前,我们还需要学习有关条件变量相关的知识,不过在学习条件变量之前,此时我们先来谈谈有关线程安全和可重入问题,当然具体为什么要学习条件变量,待会我们会娓娓道来,现在先让我们来看看这部分的知识吧!

这部分知识虽然只是概念问题,但是我觉得无论是后期线程相关的实操,还是以后复习,概念问题都不可以马虎,很多问题之所以深入不了我认为本质还是相关概念的问题,遗忘归遗忘,但是有没有踏踏实实就是另一回事了。首先,此时我们明白什么叫做线程安全,什么又叫做线程安全问题。线程安全指的是在多线程环境下,所有线程访问共享资源时不会出现竞态条件问题,也就是所有线程有序地对共享资源进行访问。同理,反之称为线程安全问题,也就是多个线程对某一共享资源进行访问,由于竞态条件(线程间执行速度和调度顺序不确定,导致线程彼此间的相对速度不同),导致共享资源数据可能被修改,从而造成无法预测的结果。

搞定了上述有关线程安全和线程安全问题相关的知识,该部分还需要将可重入问题了解一下,在之前学习有关信号知识时,我们学习了什么是可重入函数,并且通过链表的例子充分表明了什么是不可重入函数,同理,学习到线程我们就能很好的理解,为什么函数之间分为不可重入函数和可重入函数,因为在多线程环境下,线程间共享数据,如果一个函数接口被多个线程重入出现问题(内存泄露等),那么该接口就是我们说的不可重入函数,反之可重入。所以为了区分这块知识,就有了相关概念,当然面对这一问题肯定是有解决方案的,本质来说不是为了解决不可重入函数的问题,而是解决线程安全问题,也就是对共享资源的一个保护,我们可以通过互斥锁、条件变量、信号量 的使用来保护共享资源,互斥锁相关的互斥概念在上一篇博客中,我们已经详细讲解过了,条件变量相关知识下文重点介绍,信号量相关知识下篇博客重点介绍,总而言之:无论是互斥锁、条件变量、信号量,还是其它七七八八的方法,本质目的只有一个(相信这里有人会以为我要写保护共享资源,哈哈,肤浅,实力博主怎么可能水文)实现多线程间的同步。当然此同步非彼同步,具体详情,条件变量详解。

重点 搞定了上述知识,正式进入该部分的重点,可以很好的检测你到底有没有搞定上述知识哦!上述我们说了当一个函数被多个线程同时访问不会出现线程安全问题时,这个函数就被称为可重入函数,反之不可重入函数,不置可否!那么此时,你知道具体什么是可重入函数,什么是不可重入函数了吗?你没有,我也没有,假如你说你明白了,那好我问你,如果一个函数里面有一个全局变量或者是静态变量,那么该函数是可重入函数还是不可重入函数,三秒钟告诉我!错,答案是都有可能,按照正常思路来想,答案应该是不可重入函数,因为如果一个函数中存才全局变量或者静态变量,那么就会导致多个线程在访问该函数时,对同一共享资源(全局变量)进行修改,最终导致线程安全问题。反之,同理上述所说,因为我们可以使用一些同步机制来实现线程间有序访问,从而让共享资源得到保护,让一个使用了全局变量/静态变量的函数被称为可重入函数。明白了这点之后,我再问一个问题看你是否能够答对,可重入函数被多个线程重入会导致线程安全问题吗?面对这个问题,不谈答案,首先直接懵逼,很多同学会想,如果一个可重入函数被多个线程重入会导致线程安全问题,那这个函数不就不是可重入函数吗?这么想肯定是合理的,但是是存在问题的,因为通过上个问题我们知道,可重入函数不一定不可以有全局变量/静态变量,所以我们明白如果一个可重入函数中含有共享资源,那么它就必须包含同步机制,但就算包含了同步机制,该函数也不一定是可重入函数(因为共享资源可能会被修改), 所以对于我们来说,一个函数是不是可重入函数并不重要,重要的是该函数内部是否存在共享资源以及对共享资源的访问方式和是否存在线程安全措施。 也就是说,如果一个函数里面包含了共享资源,但是我采取了线程安全措施(同步机制),并且每个线程对其的访问方式只读(没有修改),那么该函数也能被称为可重入函数。同理,如果一个函数内部只含有局部变量,那么该函数不涉及共享资源,多个线程对该函数的调用也不会导致线程安全问题,该函数也被称为可重入函数。

当然对于上述知识,有的同学可能存在一定的疑问,就是为什么全局变量被称为共享资源,而局部变量则不是共享资源,这里我给你们解惑一下,在之前学习的知识中讲过只是没有重点强调,也就是因为每一个线程都有自己的上下文和栈空间,当一个线程需要访问某个函数接口时,该线程就会根据对应函数的地址找到代码段上对应内存中函数接口的代码,将其加载到自己的上下文中(寄存器),并且根据对应的代码就会将代码中的局部变量拷贝一份到自己的栈空间中,从而实现局部变量之间的互不干扰,当然之前我们说过,对于新线程,也就是刚刚创建的线程,它使用的是线程库提供的栈空间,只有主线程使用的才是地址空间中的栈空间。

死锁问题

搞定了上述有关线程安全的问题之后,此时我们来看看什么是死锁。死锁的概念非常简单,并不值得我们深思,但是与其相关的线程安全知识需要我们探讨。简简单单,我们从为什么会出现死锁问题出发,这个问题对于我们来说并没有成本,因为想要学习多线程,就一定需要解决多线程的并发问题,也就是线程安全问题,为了解决线程安全问题,我们就可以使用加锁的方式(互斥量),使用加锁,那么此时就一定会伴随死锁问题。

死锁的情况有非常多种,这里我们重点强调最经典的,明白了这点,其它死锁情况都是有理可依的,死锁指的是两个或多个线程因争夺系统资源而陷入阻塞状态。 这句话如何理解呢?首先需要明白,每个线程在执行代码时,不仅只需要访问一份共享资源,而是需要同时访问多份共享资源,这也就意味着每个线程需要同时加好几把锁。所以,当多个线程在同时执行代码访问共享资源的时候,就需要竞争这些临界资源,而如果当一个线程需要的临界资源被另一个线程占用的话,此时它就需要等待另一个线程使用完,并且解锁之后,再去获取,从而让线程执行下去。但是,如果此时另一个线程想要继续执行下去,也需要获取等待线程已经获取到的临界资源,那么它们两个此时就会因为互相竞争而导致死锁问题。也就是线程A需要两份临界资源,此时它已经竞争到其中一份,线程B也需要两份临界资源,它也竞争到一份,而它们剩下需要的那份已经被互相占有,那么此时就会导致死锁问题。 所以对于我们来说,发生死锁的必要条件就是:互斥(一份资源只能同时被一个线程访问)、请求与保持(等待资源的同时,保持已拥有的资源)、环路等待(线程间互相等待对方的资源)、不剥夺条件(已拥有资源不被其它线程访问)。

注意: 明白上述有关死锁的概念,此时我们要明白线程加锁之后未解锁并不是死锁,未解锁的本质是让其它需要访问该临界资源的线程拿不到该资源,从而导致对应线程的一个阻塞情况,同理,重复加锁情况,本质也是因为在获取资源的加锁过程,该资源可能已经被其它线程获取,导致该线程获取不到,被阻塞。这两种情况都只是线程阻塞,并不意味着死锁。 所以不能只是把因为某个线程被挂起,导致该线程占用的临界资源不能被其它线程访问,导致挂起阻塞,称之为死锁。

如何解决死锁问题呢?
这个问题本来我不是很想讲的,奈何优质创作者,哈哈哈!当然只是简单讲解一下,因为死锁问题本质我们基本碰不到,概念清晰就行,解决方法对于我们来说就有点多余,本质想要解决死锁问题,就要从死锁的四个必要条件出发,只要可以避免这4个必要条件中的任何一个,死锁问题就能解决。首先是互斥条件,最好的解决方法就是不使用互斥量,也就是不使用加锁,哈哈哈!一个非常聪明的方法,也就是说,如果以后存在加锁和不加锁的两种代码实现方法,我们应该优先考虑不加锁。其次是请求与保持,想要避免这个问题,我们可以采取主动释放锁的方式,不过想要主动释放锁,那么就涉及到pthread_mutex_trylock接口,这个接口的作用就是以非阻塞的形式去尝试访问一份资源,也就是尝试去申请锁,如果该临界资源被占用,那么该接口返回一个EBUSY错误码,表示获取失败,成功则返回0,我们再通过条件判断,判断下一步操作是获取临界资源(锁),还是释放已经占用的临界资源(pthread_mutex_unlock),从而解决请求与保持的情况,避免死锁问题。然后是环路等待,解决这个问题最好的方法就是按照顺序去申请锁,也就是让每一个线程都按照一个特定的顺序去申请锁,这样就可以避免不同线程占用其它线程的资源了,如每个线程都按照先1后2再3的顺序申请,这样一个线程拥有了1和2,就不需要担心另一个线程没有1和2只有3了,也就是不需要担心它们会互相竞争。最后是不剥夺条件,解决这个问题最好的方法就是控制线程统一释放锁,让其它线程来统一释放所有的锁,从而避免死锁问题。当然有关解决死锁问题的方法还有非常多,这里不多赘述,具体如何实操,有待深入。

条件变量

在学习什么是条件变量之前,通过上述的有关描述,此时我们需要明白两点,一点是为什么需要条件变量,一点是条件变量的本质还是在实现线程同步机制。所以此时为了弄清为什么需要条件变量,我们就需要进行额外知识的了解。并且回应上文,这里对同步的概念进行误区纠正,虽然在现实生活中,同步的意思表示同时进行,但是在计算机中或者说对于线程来说,同步指的不是同时运行,而是按照一定的顺序进行,反之异步我们才称为同时进行(信号部分重点讲过)。

为什么需要有条件变量?
首先我们来认识一个新的概念,叫线程饥饿问题,也就是某个线程由于各种原因,导致获取不到对应所需的资源,从而一直处于无法执行状态。一般导致饥饿问题的原因很简单,就是线程的优先级高低不同导致,在人为不对线程TCB中的优先级进行设置的情况下,每个线程被调度的优先级高低都是由操作系统默认随机设置的,然后调度器再根据优先级的高低去执行对应的线程的上下文代码,而此时就有可能会导致线程饥饿问题,也就是当优先级高的那个线程访问完临界资源之后,可能因为该线程的时间片还未结束,导致频繁加锁访问该临界资源(因为其优先级高),从而让那些优先级低的线程访问不到对应的共享资源(抢不到锁)造成线程饥饿问题。当然对于体系结构来说,这个现象是天生就存在的,但是这个现象是不合理的,所以为了解决这一问题,自然而然就有了条件变量的概念。

当然上述原因是条件变量产生的重要原因之一,但是并不能说条件变量就是为了解决线程饥饿问题而产生的,条件变量的概念和互斥量的概念往往是同时出现的。我们通过互斥锁实现了线程间的互斥,保护了共享资源,但是在具体的场景使用下,单单只使用互斥锁并不能很好的将线程控制住,而是需要让互斥锁和条件变量配合使用,这样才能很好的将一份代码按照我们的逻辑(规则)运行下去,这部分知识在生产者消费者模型中着重体现。这里我们只需要明白一个点,也就是一份代码单独使用互斥锁是控制不住的,只有配合条件变量,才能让每一个线程按照我们的预期执行,这也是条件变量产生的重要原因之一。

什么是条件变量呢?
明白了上述有关为什么要有条件变量的知识,此时我们学习条件变量可以说是顺理成章,上述我们说了,条件变量就是为了解决饥饿问题和配合互斥锁使用,那么到底是如何解决,又是怎样配合的呢?此时我们就需要明白条件变量的具体使用规则,其中重要的接口有两个,分别是pthread_cond_waitpthread_cond_signal,具体使用方式同理互斥锁的使用,这里不重点强调,我们重点明白使用规则就行。在对应的临界资源中,我们可以设置一个判断条件(while/if),当线程获取到锁执行相应临界资源时,按照预期对我们设置的条件进行判断,如果满足/不满足(自己设计)我们就调用pthread_cond_wait接口将该线程阻塞,只有当该线程在符合我们预期的情况下,我们才使用pthread_cond_signal接口将其唤醒,让其继续向后执行。通过这一套操作,也就是条件变量的使用,此时我们就能很好的将饥饿问题给解决,也就是一个线程光光是优先级高(抢锁能力强)已经不管用了,它还需要满足条件变量才能对临界资源进行访问,否则就会阻塞,从而让其它线程也有机会访问到该临界资源。同理第二点配合互斥锁的使用,在生产者消费者模型中体现的淋漓尽致,但是下述有一份简单的demo代码,如下:

在这里插入图片描述
注意: 对于条件变量来说一共有4个接口,它们分别是pthread_cond_init、pthread_cond_signal、pthread_cond_wait、pthread_cond_broadcast,但是其关键作用的只有两个,也就是阻塞接口和唤醒接口,其中pthread_cond_signal接口和pthread_cond_broadcast接口的区别在于前者是一个一个的对线程进行唤醒操作,而后者是一次性唤醒所有阻塞线程。最后,需要格外注意的是pthread_cond_wait接口的调用方式,其它条件变量接口只要有对应的条件变量参数就行,唯独它不同,它不仅要有条件变量参数,还需要有互斥锁,也就是说,我们需要将互斥锁的地址给给该接口,当阻塞某个线程时,由于该线程肯定是先申请锁,所以它已经拥有了该锁,所以此时我们需要拿到锁的地址,将对应的锁给释放掉,否则就会导致其它线程由于拿不到锁而被阻塞问题。所以pthread_cond_wait不仅有阻塞线程的功能,它还有解锁功能。

生产者消费者模型

搞定上述有关条件变量等知识,此时我们就可以正式进入生产消费模型的学习,因为生产消费模型本质就是利用了互斥锁和条件变量这两个同步机制来实现的,同理上述学习条件变量有关知识的风格,此时我们第一点先来了解一下为什么要学习生产者消费者模型,当然也就是为什么有生产者消费者模型这个概念。

为什么要有生产者消费者模型?

首先从名字出发,生产者和消费者对于计算机而言可以说是非常的抽象,所以想要搞懂这块知识,同理万能学习方法,图示或者是类比,此时我们使用类比,对于生产消费模型来说此时我们可以将其类比为通信,生产者向同一份资源(缓冲区)中放数据,消费者从同一份资源(缓冲区)中拿数据,只不过此时的对象不再是进程,而是线程,所以生产消费模型的本质就是一种线程间的通信而已,只不过此时它有了一个更好听的名字,那么为什么要叫生产消费模型呢?具体有什么含义呢?生产消费模型又有什么好处呢?容我一点一点的细细道来,首先我们看一幅图,如下所示:

只要是有一定生活常识的人都知道,这是一幅超市的图片,在超市中包含了非常多的商品,并且这些商品都是从各种工厂中被生产出来的,此时我们就把那些工厂称作生产者,而超市中的顾客我们就称作消费者。从这点来看,此时我们对生产消费模型好像有了更深的认识,因为超市的作用我们都知道,它就是用来存放各种商品,然后供给消费者获取的。所以此时对于生产消费模型来说,其中的同一份资源我们就把它看做是一个超市,其中不同执行方式的线程我们就看做是一个一个的生产者或者是消费者,如下图所示:

此时通过上图,我们来认真的分析一下生产消费模型的含义,以及它的好处。首先我们知道由于缓冲区(超市)受到同步机制保护,但这并不表示缓冲区(超市)每次只允许一个线程进入,类比日常生活中的超市我们也知道,这种情况是不允许的,所以也就是所有线程都能看到且访问该同一份资源(缓冲区),但是可能因为缓冲区中有许多不同的区域,每一个区域被同步机制保护,区域内的资源一次只允许一个线程访问(无论是读取/写入),具体如何控制该缓冲区和缓冲区内不同的区域,都是由设计者自己通过同步机制代码实现的。但是要注意在我们的生产消费模型中,并不存在一边生产,一边消费的情况,类比生活中的超市,由于超市本身可以存储商品,所以不需要一边进货一边卖货,一定是等商品全部买完了之后,再去找生产者生产,所以对于我们来说生产者和消费者之间的关系是一种互斥且同步的关系。所以在我们看来,生产消费模型本质就是存在三种关系:消费者和消费者之间的互斥关系、生产者和生产者的互斥关系、生产者和消费者的同步且互斥关系。也就是类似于超市里,顾客和顾客之间可能会因为同一件商品而处于竞争关系、工厂和工厂之间同理竞争关系、顾客和工厂由于不能同时出现在超市,所以是一种同步且互斥关系,由于同步,所以互斥。

明白了上述的三种关系之后,此时我们对生产消费模型的理解初步完成,也就是说当我们在使用代码构建自己的生产消费模型时,我们就需要按照这三种关系来构建,也就是对缓冲区内的某一块资源进行加锁,让不同的线程互斥,对整个缓冲区使用条件变量,从而实现消费者和生产者的同步机制,本质也就是整个缓冲区由于生产者和消费者的原因,一定需要使用条件变量来控制同步机制,但是整个缓冲区并不一定都需要互斥,只需要对缓冲区内不同区域的资源进行互斥保护就行,具体由设计者决定。最后再来看看生产消费模型的优点,首先第一个优点是实现了不同线程间的解耦,也就是消费者和生产者之间的解耦,解耦也就是让两个线程之间的依赖关系降低,此时一个线程需要从另一个线程获取数据,此时就不需要花费时间去等待另一个线程,而是可以直接从缓冲区内获取,其次当一个线程在等待访问缓冲区时,其他线程可以继续执行其它任务,不需要等待。也就是当某个线程完成对缓冲区的访问之后,其它线程可以立即开始访问该缓冲区,不需要再等待别的共享资源,因为在等待访问该缓冲区资源时,该线程已经通过并发访问的形式,获取到了其余资源,这样可以大幅度的提高系统的响应速度,提高系统效率。

生产消费模型代码

基于BlockintQueue的生产消费模型

在这里插入图片描述

总结:由于时间问题,最后有关生产消费模型的代码并没有进行深入讲解,留到下篇博客我们再继续吧!See you!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/56811.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

DAY56:单调栈(二)下一个最大元素Ⅱ(环形数组处理思路)

文章目录 思路写法1完整版环形数组处理:i取模,遍历两遍写法2完整版(环形数组推荐写法)debug测试:逻辑运算符短路特性result数组在栈口取元素,是否会覆盖原有数值? 给定一个循环数组 nums &#…

Unity 性能优化五:渲染模块压力

CPU压力 Batching 在GPU渲染前,CPU会把数据按batch发送给GPU,每发送一次,都是一个drawcall,GPU在渲染每个batch的时候,会切换渲染状态,这里的渲染状态指的是:影响对象在屏幕上的外观的渲染属性…

竞速榜实时离线对数方案演进介绍 | 京东云技术团队

一、背景 竞速榜是大促期间各采销群提供的基于京东实时销售数据的排行榜,同样应对大促流量洪峰场景,通过榜单撬动品牌在京东增加资源投入。竞速榜基于用户配置规则进行实时数据计算,榜单排名在大促期间实时变化,相关排名数据在微…

vscode添加自定义的用户代码片段

在vscode中添加代码片段 选择“新建全局代码片段文件,然后输入文件名(随便输入) 然后会生成文件,安装文件中的Example就可以添加代码片段 里面各个字段的含义: "Print to console:代码片段的名称&…

迷你主机中的战斗机 Intel NUC 12 Serpent Canyon拆解

千呼万唤始出来,新一代游戏和创作者性能怪兽 mini主机 NUC 12 Serpent Canyon(巨蛇峡谷终于发售了,以超紧凑的 2.5 升尺寸提供用户所需的所有性能和创新功能。NUC 12 Enthusiast 还首次将 Intel Deep Link 引入桌面,使 CPU 和 GPU…

【雕爷学编程】Arduino动手做(181)---Maixduino AI开发板5

37款传感器与执行器的提法,在网络上广泛流传,其实Arduino能够兼容的传感器模块肯定是不止这37种的。鉴于本人手头积累了一些传感器和执行器模块,依照实践出真知(一定要动手做)的理念,以学习和交流为目的&am…

git常用指令

git add命令 作用:移动文件:工作区-->暂存区 git add .:把所有文件都放到暂存区 git commit命令 作用:移动文件:暂存区-->本地仓库 git status命令 作用:查看修改状态 git log命令 作用&#xf…

天津web前端开发培训班 零基础如何学习前端?

学习Web前端有很多好处,它可以提高你的数字技能,使你更具有竞争力,而且Web前端是一个需求量很大的岗位,有这项技能在手,你可以轻松地找到一份工作。 什么是web前端 前端开发是创建web页面或app等前端界面给用户的过程…

大数据学习教程:Linux 高级教程(上)

一、Linux用户与权限 1. 用户和权限的基本概念 1.1、基本概念 用户 是Linux系统工作中重要的一环, 用户管理包括 用户 与 组 管理 在Linux系统中, 不论是由本级或是远程登录系统, 每个系统都必须拥有一个账号, 并且对于不同的系统资源拥有不同的使用权限 对 文件 / 目录 的…

Spring之事务实现方式及原理

目录 Spring事务简介 Spring支持事务管理的两种方式 编程式事务控制 声明式事务管理 Spring事务角色 未开启事务之前 开启Spring的事务管理后 事务配置 事务传播行为 事务传播行为的可选值 Spring事务简介 事务作用:在数据层保障一系列的数据库操作同成功…

VR 变电站事故追忆反演——正泰电力携手图扑

VR(Virtual Reality,虚拟现实)技术作为近年来快速发展的一项新技术,具有广泛的应用前景,支持融合人工智能、机器学习、大数据等技术,实现更加智能化、个性化的应用。在电力能源领域,VR 技术在高性能计算机和专有设备支…

【多模态】21、BARON | 通过引入大量 regions 来提升模型开放词汇目标检测能力(CVPR2021)

文章目录 一、背景二、方法2.1 主要过程2.2 Forming Bag of Regions2.3 Representing Bag of Regions2.4 Aligning bag of regions 三、效果 论文:Aligning Bag of Regions for Open-Vocabulary Object Detection 代码:https://github.com/wusize/ovdet…

网络安全进阶学习第九课——SQL注入介绍

文章目录 一、什么是注入二、什么是SQL注入三、SQL注入产生的原因四、SQL注入的危害五、SQL注入在渗透中的利用1、绕过登录验证:使用万能密码登录网站后台等。2、获取敏感数据3、文件系统操作4、注册表操作5、执行系统命令 六、如何挖掘SQL注入1、SQL注入漏洞分类按…

LLaMA系列 | LLaMA和LLaMA-2精简总结

文章目录 1、LLaMA1.1、模型结构1.2、训练方式1.3、结论 2、LLaMA-22.1、相比LLaMA1的升级2.3、模型结构2.3.1、MHA, MQA, GQA区别与联系 2.4、训练方式 1、LLaMA 🔥 纯基座语言模型 《LLaMA: Open and Efficient Foundation Language Models》:https:/…

大麦链接源码 大麦一键生成订单截图

8.4最新版源码 更新了大麦链接模版 更新了大麦订单截图一键生成 下载源码:https://pan.baidu.com/s/16lN3gvRIZm7pqhvVMYYecQ?pwd6zw3

Android 自定义按钮(可滑动、点击)

按钮图片素材 https://download.csdn.net/download/Lan_Se_Tian_Ma/88151085 px 和 dp 转换工具类(Java) // px 和 dp 转换工具类 public class DensityUtil {/*** 根据手机的分辨率从 dip 的单位 转成为 px(像素)*/public static int dip2px(Conte…

Excel技巧 - 管理规则设置一行变色

如何设置某一列单元格的值大于一个值时,该单元格所在的一整行都变色呢? 1、先框选内容区域,点击开始,条件格式,新建规则 2、如果销量大于20,则该行都变为绿色 编辑格式选择:使用公式确定要设置…

springboot第33集:nacos图

./startup.sh -m standalone Nacos是一个内部微服务组件,需要在可信的内部网络中运行,不可暴露在公网环境,防止带来安全风险。Nacos提供简单的鉴权实现,为防止业务错用的弱鉴权体系,不是防止恶意攻击的强鉴权体系。 鉴…

ChatGPT及其工作原理;OpenAI申请注册商标GPT-5,引发关注

🦉 AI新闻 🚀 OpenAI申请注册商标GPT-5,引发关注 摘要:OpenAI已在上月18日申请注册商标GPT-5,显示该模型将提供文本生成、自然语言理解、语音转录、翻译、分析等功能。此前OpenAI曾表示尚未开始训练GPT-4的后继者GPT…

在 React 中渲染大型数据集的 3 种方法

随着 Web 应用程序变得越来越复杂,我们需要找到有效的方法来优化性能和渲染大型数据集。在 React 应用程序中处理大型数据集时,一次呈现所有数据可能会导致性能不佳和加载时间变慢。 虚拟化是一种通过一次仅呈现数据集的一部分来解决此问题的技术&#…