当前位置: 首页 > news >正文

网站建设需要公司中国建设银行注册网站用户名怎么填

网站建设需要公司,中国建设银行注册网站用户名怎么填,施工企业安全生产评价标准最新版2021,高端网站建设 案例欢迎各位大佬光临本文章#xff01;#xff01;#xff01; 还请各位大佬提出宝贵的意见#xff0c;如发现文章错误请联系冰冰#xff0c;冰冰一定会虚心接受#xff0c;及时改正。 本系列文章为冰冰学习编程的学习笔记#xff0c;如果对您也有帮助#xff0c;还请各位… 欢迎各位大佬光临本文章 还请各位大佬提出宝贵的意见如发现文章错误请联系冰冰冰冰一定会虚心接受及时改正。 本系列文章为冰冰学习编程的学习笔记如果对您也有帮助还请各位大佬、帅哥、美女点点支持您的每一分关心都是我坚持的动力。 我的博客地址bingbing~bang的博客_CSDN博客https://blog.csdn.net/bingbing_bang?typeblog 我的gitee冰冰棒 (bingbingsupercool) - Gitee.comhttps://gitee.com/bingbingsurercool 系列文章推荐 冰冰学习笔记《信号》 冰冰学习笔记《管道与共享内存》 目录 系列文章推荐 前言 1.Linux的线程概念 2.线程与进程的对比 2.1线程的优缺点 2.2线程的异常和用途 2.3进程与线程的资源划分 3.线程控制 4.线程互斥 4.1为什么需要线程互斥 4.2互斥量的函数接口 4.3深入理解申请和释放锁 4.4可重入函数和线程安全 4.5死锁概念 5.线程同步 5.1为什么需要线程同步 5.2条件变量函数 6.POSIX信号量 6.1信号量操作函数 6.2环形队列的生产消费模型 前言 之前我们学到过进程的概念进程是系统调度的基本单位每个进程都有自己独特的PCB机构以及自己独有的内存空间。当我们想要其他进程去执行任务时我们可以创建子进程去执行父进程分配给子进程的任务子进程与父进程的代码和数据虽然相同但是子进程的数据是父进程的拷贝子进程修改并不影响父进程。今天我们说讲的线程类似于子进程也是一个执行流用来执行不同任务但是与进程有所区别。 1.Linux的线程概念 在将Linux的线程之前我们再重新认识以下进程的概念。 每一个进程都有自己独有的task_struct结构体结构体中具备该进程自己的虚拟空间地址并通过页表映射到物理内存中。我们之前并没有详细讲解页表的存储方式页表是如何映射这么多的地址空间的呢 如果页表一一映射物理内存地址那么页表非常大内存根本无法存储。因此页表采用了分成的映射方式。物理内存实际上是按照4kb的单位进行划分的每一小块内存称之为页框磁盘上的内存也是按照4kb划分称之为页帧。页表想要映射这些地址显然是无法存储的。 页表分为两层进行映射页表将4字节32个比特位划分为3组第一组为前10个比特位存储在一级页表中。中间10个比特位映射到2级页表中。这样一个地址可以根据前10个位找到一级页表通过一级页表找到对应的二级页表根据中间10个比特位就能找到物理内存中对应的哪个4kb空间。最后12个比特位则是每个4kb空间的偏移量通过每个偏移量则能找到每个地址。 因此CPU在调度时找到进程的task_struct结构体通过结构体访问虚拟地址然后通过页表的映射最终访问到物理内存中的数据。而当我们创建子进程时子进程会拷贝父进程的task_struct虚拟地址空间页表并重新映射自己的物理内存。CPU调度子进程从而通过映射后找到的是子进程对应的物理地址中的数据。 而此时我们发现CPU调度时不会管你的虚拟地址空间是不是自己的我只需要你的task_struct结构体即可。 其实每一个进程中的task_struct结构体都称之为一个线程即在一个程序里的一个执行路线就叫做线程thread。更准确的定义是线程是“一个进程内部的控制序列”。因此Linux的线程与进程没有多大的区别只不过每个进程有自己独有的虚拟地址空间而多线程则共享一个进程中的虚拟地址空间即线程在进程的地址空间内运行。 所以之前我们学的进程实际上是内部只有一个执行流的进程而内部具备多个执行流时每个执行流就叫做线程。CPU只管调度task_struct并不管具备几个执行流。所以我们看到线程实际上才是OS调度的基本单位。 Linux没有真正意义上的线程结构有的只是轻量级的进程因此Linux并不能给我们提供线程的相关接口只能提供轻量级的进程接口。但是Linux为了方便使用在用户层实现了一套多线程的方案即pthread库。 2.线程与进程的对比 2.1线程的优缺点 线程的优点 1创建一个新线程的代价要比创建一个新进程小得多 2与进程之间的切换相比线程之间的切换需要操作系统做的工作要少很多。 3线程占用的资源要比进程少很多 4能充分利用多处理器的可并行数量 5在等待慢速I/O操作结束的同时程序可执行其他的计算任务 6计算密集型应用为了能在多处理器系统上运行将计算分解到多个线程中实现 7I/O密集型应用为了提高性能将I/O操作重叠。线程可以同时等待不同的I/O操作。 线程的缺点 线程有可能照成性能损失如果计算密集型 线程的数量比可用的处理器多那么可能会有较大的性能损失这里的性能损失指的是增加了额外的同步和调度开销而可用的资源不变。 程序健壮性降低在一个多线程程序里因时间分配上的细微偏差或者因共享了不该共享的变量而造成不良影响的可能性是很大的换句话说线程之间是缺乏保护的。 线程缺乏访问控制进程是访问控制的基本粒度在一个线程中调用某些OS函数会对整个进程造成影响。 线程的编写难度提高编写与调试一个多线程程序比单线程程序困难得多。 2.2线程的异常和用途 单个线程如果出现除零野指针问题导致线程崩溃进程也会随着崩溃。线程是进程的执行分支线程出异常就类似进程出异常进而触发信号机制终止进程进程终止该进程内的所有线程也就随即退出。 但是合理的使用多线程能提高CPU密集型程序的执行效率合理的使用多线程能提高IO密集型程序的用户体验。 2.3进程与线程的资源划分 进程的多个线程中绝大多数的资源都是共享的如代码段数据段或者定义的一个函数、全局变量各个线程都能调用。线程还共享文件描述符表每种信号的处理方式当前工作目录用户id和组id。 但是进程是资源分配的基本单位线程是调度的基本单位线程也具备自己的数据如线程ID一组寄存器栈信号屏蔽字errno调度优先级。 3.线程控制 与线程有关的函数构成了一个完整的系列绝大多数函数的名字都是以“pthread_”打头的。要使用这些函数库要通过引入头文件pthread.h 。链接这些线程函数库时要使用编译器命令的   “-lpthread”选项。 1pthread_create创建新线程 头文件#includepthread 函数体int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void * (*start_routine)(void*), void *arg); 参数thread:返回线程ID            attr:设置线程的属性attr为NULL表示使用默认属性            start_routine:是个函数地址线程启动后要执行的函数            arg:传给线程启动函数的参数 返回值成功返回0失败返回错误码。 注意 传统的一些函数是成功返回0失败返回-1并且对全局变量errno赋值以指示错误。 pthreads函数出错时不会设置全局变量errno而大部分其他POSIX函数会这样做。而是将错误代码通过返回值返回 pthreads同样也提供了线程内的errno变量以支持其它使用errno的代码。对于pthreads函数的错误 建议通过返回值业判定因为读取返回值要比读取线程内的errno变量的开销更小 下面我们通过代码创建多个线程并验证多线程是否在同一个进程中 void* handler(void* name) {const string s(char*)name;while(true){couts进程id为getpid()endl;sleep(1);} } int main() {pthread_t tid[5];char name[64];for(int i1;i5;i){snprintf(name,sizeof(name),%s-%d,thread,i);//创建多个线程pthread_create(tid[i-1],nullptr,handler,(void*)name);sleep(1);}while(true){cout主线程pid getpid()endl;sleep(5);}return 0; } 结果发现每个线程的pid与主进程的pid完全相同这意味着线程在进程内部。  当线程出现野指针除零错误时进程会不会崩溃呢 void* handler(void* name) {const string s(char*)name;int count0;while(true){couts在运行:countendl;count;sleep(1);if(count5){char* pnullptr;*pa;//野指针问题}} } int main() {pthread_t tid;pthread_create(tid,nullptr,handler,(void*)thread-1);while(true){cout主进程在运行endl;sleep(1);} } 在线程执行5秒后出现野指针错误此时我们发现一直在运行的两个线程都会退出。线程虽然pid相同但是每个线程都有自己独特的LWP轻量级进程号CPU通过LWP进行调度。 而且我们还发现线程的执行并没有固定的顺序例如在第一个例子中线程完全没有顺序这就说明线程的运行顺序和调度器有关。线程一旦异常都可能导致整个进程体系退出线程在创建并执行的时候线程也是需要等待的如果不等待也会出现类似于僵尸进程的问题导致内存泄漏。 线程之间对于全局变量也是共享的一个线程更改其他线程的数据也会更改如果想让全局变量每个线程私有那么需要增加__thread进行修饰。 int g_val0; void* handler(void* num) {while(true){cout新线程g_val g_valendl;sleep(1);} } int main() {pthread_t tid;pthread_create(tid,nullptr,handler,(void*)thread-1);while(true){cout主线程g_val g_valendl;sleep(1);} } 新线程对g_val进行更改此时两个线程都会更改 当使用__thread修饰后新进程更改不影响主线程 2pthread_join线程等待 头文件#includepthread.h 函数体int pthread_join(pthread_t thread, void **value_ptr); 参数thread:线程ID            value_ptr:它指向一个指针后者指向线程的返回值 返回值成功返回0失败返回错误码 为什么需要线程等待呢原因在于已经退出的线程其空间没有被释放仍然在进程的地址空间内。 创建新的线程不会复用刚才退出线程的地址空间。 线程在创建后会去执行线程对应的功能函数该函数是具备返回值的那么函数的返回值返回给谁呢其实返回值就返回给了创建线程的进程并且通过pthread_join函数的第二个参数获取。线程等待是默认以阻塞的方式进行等待如果线程不退出就会一直等待。 用下面的代码进行验证 void* handler(void* name) {const string s(char*)name;int count0;int* arrnew int[5];while(true){couts在运行:countendl;arr[count]count;if(count5)break;sleep(1);}return (void*)arr; } int main() {pthread_t tid;pthread_create(tid,nullptr,handler,(void*)thread-1);int *arr;pthread_join(tid,(void**)arr);//默认阻塞等待cout主线程获取返回值endl;for(int i0;i5;i){coutarr[i] ;}coutendl; } 主线程获取到返回值并打印 thread线程以不同的方法终止通过pthread_join得到的终止状态是不同的总结如下: 1. 如果thread线程通过return返回value_ ptr所指向的单元里存放的是thread线程函数的返回值。         2. 如果thread线程被别的线程调用pthread_ cancel异常终掉value_ ptr所指向的单元里存放的是常数 PTHREAD_ CANCELED。         3. 如果thread线程是自己调用pthread_exit终止的value_ptr所指向的单元存放的是传给pthread_exit的参数。         4. 如果对thread线程的终止状态不感兴趣可以传NULL给value_ ptr参数。 3线程终止pthread_exit 头文件#includepthread.h 函数体void pthread_exit(void *value_ptr); 参数value_ptrvalue_ptr不要指向一个局部变量。 返回值无返回值跟进程一样线程结束的时候无法返回到它的调用者自身 线程的终止函数不能直接调用exit函数该函数意味着进程的终止如果在线程退出时调用整个进程就会退出。 4线程取消pthread_cancel 头文件#includepthread.h 函数体int pthread_cancel(pthread_t thread); 参数thread:线程ID 返回值成功返回0失败返回错误码 线程取消时一定要主线程取消新线程并且确保新线程已经开始运行了。  void* handler(void* name) {const string s(char*)name;int count0;while(true){couts运行中 countendl;count;if(count5)break;sleep(1);}cout线程终止endl;pthread_exit((void*)2); } int main() {pthread_t tid;pthread_create(tid,nullptr,handler,(void*)thread-1);sleep(3);pthread_cancel(tid);cout3秒后线程取消endl;pthread_join(tid,nullptr);//默认阻塞等待 } 5获取线程id pthread_self 头文件#includepthread.h 函数体pthread_t pthread_self(void); 参数无参 返回值返回当前线程的线程id 对于Linux目前实现的NPTL实现而言pthread_t类型的线程ID本质 就是一个进程地址空间上的一个地址。线程之间的栈是不共享的那么每个线程的栈是怎么维护的呢 其实pthread库中给线程维护了一个独立的栈空间而该空间的地址就是pthread_t类型的线程id。 6线程分离pthread_detach 头文件#includepthread.h 函数体int pthread_detach(pthread_t thread); 参数线程id 返回值成功返回0错误返回错误码。 默认情况下新创建的线程是joinable的线程退出后需要对其进行pthread_join操作否则无法释放资源从而造成系统泄漏。 如果不关心线程的返回值join是一种负担这个时候我们可以将线程分离这就告诉系统当线程退出时自动释放线程资源。 4.线程互斥 4.1为什么需要线程互斥 在了解线程互斥之前我们先复习之前讲过的一些概念 1临界资源多线程执行流共享的资源就叫做临界资源 2临界区每个线程内部访问临界资源的代码就叫做临界区 3互斥任何时刻互斥保证有且只有一个执行流进入临界区访问临界资源通常对临界资源起保护作用 4原子性不会被任何调度机制打断的操作该操作只有两态要么完成要么未完成。 线程互斥主要解决的就是线程之间对临界资源互相访问因为线程调度时间不同而造成的数据混乱问题。大部分情况线程使用的数据都是局部变量变量的地址空间在线程栈空间内这种情况变量归属单个 线程其他线程无法获得这种变量。但有时候很多变量都需要在线程间共享这样的变量称为共享变量可以通过数据的共享完成线程之间的交互。 下面的抢票例子中多线程之间访问同一个全局变量会出现票数多卖的情况 int tickets1000; void *getTickets(void *args) {(void)args;while(true){if(tickets 0){usleep(1000);printf(%p: %d\n, pthread_self(), tickets);tickets--;}else{break;}}return nullptr; } int main() {pthread_t tid[5];char name[64];for(int i1;i5;i){//创建多个线程pthread_create(tid[i-1],nullptr,getTickets,nullptr);}for(int i0;i5;i){pthread_join(tid[i],nullptr);}return 0; } 我们发现有些线程会出现抢到负数票的情况。 这其中的原因就是多线程对不加保护的临界变量进行并发执行的问题。票数tickets进行自减的操作看似只有一行代码实际上对应三条汇编指令因此tickets的自减操作并非原子操作。CPU对tickets的操作需要分为三步第一步load 将共享变量tickets从内存加载到寄存器中第二步update : 更新寄存器里面的值执行-1操作第三步store 将新值从寄存器写回共享变量tickets的内存地址。这三步在一个线程执行过程中会有可能在任意一步进行切走执行另外的线程其他线程又会访问该变量。 多个线程经过这种不加保护的操作后tickets出现混乱从而导致票数多卖。 要解决以上问题需要做到三点 1代码必须要有互斥行为当代码进入临界区执行时不允许其他线程进入该临界区。 2如果多个线程同时要求执行临界区的代码并且临界区没有线程在执行那么只能允许一个线程进入该临界区。 3如果线程不在临界区中执行那么该线程不能阻止其他线程进入临界区。 本质上我们需要线程独立的访问临界数据区需要一把锁将该区域进行锁住Linux上提供的这把锁叫互斥量。 4.2互斥量的函数接口 1创建互斥量 静态分配pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER 锁是全局的变量时使用宏PTHREAD_MUTEX_INITIALIZER进行初始化。 动态分配当锁是局部变量时需要调用初始化函数pthread_mutex_init进行初始化。 头文件#includepthread.h 函数体int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); 参数mutex要初始化的互斥量            attrNULL 返回值成功返回0,失败返回错误号 2销毁互斥量 头文件#includepthread.h 函数体int pthread_mutex_destroy(pthread_mutex_t *mutex) 参数mutex要销毁的互斥量 返回值成功返回0,失败返回错误号 3加锁和解锁 头文件#includepthread.h 函数体int pthread_mutex_lock(pthread_mutex_t *mutex);               int pthread_mutex_unlock(pthread_mutex_t *mutex); 参数mutex要加锁或者解锁的互斥量 返回值成功返回0,失败返回错误号 此时将抢票逻辑进行加锁控制此时就不会出现数据紊乱的问题了。 void *getTickets(void *mtx) {while(true){pthread_mutex_lock((pthread_mutex_t*)mtx);if(tickets 0){usleep(rand()%1000);printf(%p: %d\n, pthread_self(), tickets);tickets--;pthread_mutex_unlock((pthread_mutex_t*)mtx);}else{pthread_mutex_unlock((pthread_mutex_t*)mtx);break;}}usleep(rand()%200000);return nullptr; } 由此我们可以得出在进行加锁之后线程之间执行临界区的代码时是串行的那么加了锁之后线程在临界区还是会进行切换的但是此时的切换是带着锁进行切换的其他线程想要访问临界区的资源还是需要先申请锁锁无法申请成功所以此时还是无法访问临界资源从而确保了临界区的资源的安全性。注意加锁的粒度需要越细越好。 4.3深入理解申请和释放锁 现在我们明白了临界区的代码添加锁后就能保证多个线程访问共享数据的唯一性也就是这把锁是每个线程都能看到的资源。那么这把锁不也是一种共享资源吗那么谁来保证锁的安全呢换句话说申请和释放锁也必须是原子性的。这就陷入了循环死穴。 其实锁的原子性是由锁本身来保证的。 在CPU执行计算时如果只有一条汇编语句那么就认为该汇编语句的执行是原子的。为了实现互斥锁操作大多数体系结构都提供了swap或exchange指令该指令的作用是把寄存器和内存单元的数据相交换由于只有一条指令保证了原子性即使是多处理器平台访问内存的总线周期也有先后一 个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。 而lock和unlock的伪代码如下所示我们进行分析 首先我们要知道多个线程共享CPU寄存器的空间但是寄存器里面的内容是每个线程的上下文数据是私有的在被切换时会带走。 整个过程中mtx的1全程只有一个线程AB都是通过交换得到的线程A交换走线程B就不会得到从而保证了原子性 。 4.4可重入函数和线程安全 线程安全 多个线程并发同一段代码时不会出现不同的结果。常见对全局变量或者静态变量进行操作 并且没有锁保护的情况下会出现该问题。 重入 同一个函数被不同的执行流调用当前一个流程还没有执行完就有其他的执行流再次进入我们称之为重入。一个函数在重入的情况下运行结果不会出现任何不同或者任何问题则该函数被称为可重入函数否则是不可重入函数。 线程不安全的情况 不保护共享变量的函数函数状态随着被调用状态发生变化的函数返回指向静态变量指针的函数调用线程不安全函数的函数。 线程安全的情况 每个线程对全局变量或者静态变量只有读取的权限而没有写入的权限一般来说这些线程是安全的类或者接口对于线程来说都是原子操作多个线程之间的切换不会导致该接口的执行结果存在二义性。 不可重入的情况 调用了malloc/free函数因为malloc函数是用全局链表来管理堆的调用了标准I/O库函数标准I/O库的很多实现都以不可重入的方式使用全局数据结构可重入函数体内使用了静态的数据结构。 可重入的情况 不使用全局变量或静态变量不使用用malloc或者new开辟出的空间不调用不可重入函数 不返回静态或全局数据所有数据都有函数的调用者提供使用本地数据或者通过制作全局数据的本地拷贝来保护全局数据。 可重入与线程安全的联系和区别 函数是可重入的那就是线程安全的函数是不可重入的那就不能由多个线程使用有可能引发线程安全问题如果一个函数中有全局变量那么这个函数既不是线程安全也不是可重入的。 可重入函数是线程安全函数的一种。线程安全不一定是可重入的而可重入函数则一定是线程安全的。 如果将对临界资源的访问加上锁则这个函数是线程安全的但如果这个重入函数若锁还未释放则会产生死锁因此是不可重入的。 4.5死锁概念 死锁是指在一组进程中的各个进程均占有不会释放的资源但因互相申请被其他进程所站用不会释放的资 源而处于的一种永久等待状态。 多个锁的申请和释放会造成死锁例如线程A申请锁1成功后去申请锁2发现锁2被线程B申请了线程A只能挂起等待而线程B在执行过程中又去申请锁1发现线程A申请了只能挂起等待此时两个线程陷入死锁互相等待。 一把锁也有可能造成死锁例如线程A申请锁之后没有释放再去申请时就会造成死锁。 死锁四个必要条件 1互斥条件一个资源每次只能被一个执行流使用 2请求与保持条件一个执行流因请求资源而阻塞时对已获得的资源保持不放 3不剥夺条件一个执行流已获得的资源在末使用完之前不能强行剥夺 4循环等待条件若干执行流之间形成一种头尾相接的循环等待资源的关系 避免死锁 1破坏死锁的四个必要条件 2加锁顺序一致 3避免锁未释放的场景 4资源一次性分配 避免死锁算法死锁检测算法银行家算法。 5.线程同步 5.1为什么需要线程同步 通过互斥锁的使用我们能够确保临界资源的安全。但是线程在使用互斥锁时还会带了一个问题如果一个线程频繁的申请互斥锁那么其他的线程就得等待线程的等待没有秩序谁抢到就是谁的。线程在申请临界资源之前一定要先对临界资源的存在做出检测而对临界资源检测的本质也是访问临界资源这就意味着对临界资源的检测也一定需要在加锁和解锁之间。那么那些等待临界资源的线程就必然需要频繁的申请和释放锁带来极大的资源浪费。 线程同步在保证数据安全的前提下让线程能够按照某种特定的顺序访问临界资源从而有效避免饥饿问题。存在的目的就是为了解决这些线程访问临界资源合理性的问题。 竞态条件因为时序问题而导致程序异常我们称之为竞态条件。 如果我们能够让线程在资源不就绪的时候进行等待而不是频繁的进行临界资源的申请等到临界资源满足条件就绪了就通知对应的线程让其来进行资源的申请和访问。这就需要条件变量。 5.2条件变量函数 1条件变量的初始化函数 当定义全局的条件变量时可以使用PTHREAD_COND_INITIALIZER进行初始化。 pthread_cond_t cond PTHREAD_COND_INITIALIZER; 当条件变量为局部变量时需要调用初始化函数进行初始化。 头文件#includepthread.h 函数体int pthread_cond_init(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr); 参数         cond要初始化的条件变量         attrNULL 返回值  成功返回0失败返回错误码 2销毁函数 头文件#includepthread.h 函数体int pthread_cond_destroy(pthread_cond_t *cond) 参数cond要销毁的条件变量 返回值  成功返回0失败返回错误码 3等待条件函数 头文件#includepthread.h 函数体int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex); 参数cond要在这个条件变量上等待           mutex互斥量 返回值  成功返回0失败返回错误码 为什么 pthread_cond_wait 需要互斥量? 条件等待是线程间同步的一种手段如果只有一个线程条件不满足一直等下去都不会满足所以必须要有一个线程通过某些操作改变共享变量使原先不满足的条件变得满足并且友好的通知等待在条件变量上的线程。 条件不会无缘无故的突然变得满足了必然会牵扯到共享数据的变化。所以一定要用互斥锁来保护。没有互斥锁就无法安全的获取和修改共享数据。 4唤醒等待 头文件#includepthread.h 函数体int pthread_cond_broadcast(pthread_cond_t *cond);//一次唤醒一批线程               int pthread_cond_signal(pthread_cond_t *cond);//一次唤醒一个线程 参数cond要唤醒的条件变量 返回值  成功返回0失败返回错误码 代码练习生产者消费者模型。 6.POSIX信号量 前面的章节中我们提到过信号量并且将其视为一个“计数器”。现在我们深入了解一下信号量。这里我们所说的信号量是POSIX信号量它可以支持线程同步。我们都知道在访问共享资源的时候对于临界区的资源必须要确保只有一个执行流来进行访问因为只有这样才是安全的。但是有时临界区具备多种临界资源每个线程想要获取的或许是不同的如果都要加锁解锁来访问效率必然降低。因此我们可以在访问前进行申请如果资源具备那线程就直接拿走其他线程同时也可以申请就如同我们买点影票一样只有里面的资源不再具备此时线程申请就会失败哪个线程都一样都必须等待。只有线程访问的资源相同时才进行加速解锁操作。 所以在对资源进行使用时我们先进行申请就是信号量的P操作使用完毕后对其进行释放就是信号量的V操作。具体的函数如下 6.1信号量操作函数 1初始化信号量 头文件#include semaphore.h  函数体int sem_init(sem_t *sem, int pshared, unsigned int value);  参数                pshared:0表示线程间共享非零表示进程间共享                  value信号量初始值  返回值  成功返回0失败返回-1并且设置错误码 2销毁信号量 头文件#include semaphore.h  函数体int sem_destroy(sem_t *sem);  参数要销毁的信号量 返回值  成功返回0失败返回-1并且设置错误码 3等待信号量 头文件#include semaphore.h  函数体int sem_wait(sem_t *sem);   参数等待的信号量 返回值  成功返回0失败返回-1并且设置错误码 4发布信号量  头文件#include semaphore.h  函数体int sem_post(sem_t *sem); 参数要发布的信号量 返回值  成功返回0失败返回-1并且设置错误码 6.2环形队列的生产消费模型 环形队列中一个线程进行数据的生产一个线程进行数据的消费如果此时两个线程访问的并非同一个数据那么就不会出现线程安全问题只有在同时访问同一个数据的时候才会出现数据二义性的问题。 例如在下面的情况线程A在生产了数据-4线程B正在拿走数据6此时两个线程并不干扰不需要加锁来进行保护。当环形队列中数据为空时线程B想要消费就必须等待线程A进行生产环形队列数据满了时线程A想生产就必须让线程B进行消费之后才能生产。 所以线程A扮演的生产者需要的是空间资源具备空间资源才能生产数据。线程B扮演的消费者需要数据资源消费了数据资源才能具备空间。所以此时我们就可以引入信号量进行生产当生产者进行生产时先去申请空间资源申请成功则空间资源信号量自减并进行数据生产生产完数据后将数据资源的信号量进行自增申请失败则需要等待空间资源就绪。同理消费者消费时也要申请数据资源成功则数据资源自减失败则说明没有数据可以消费需要等待生产者进行生产。消费成功后空间资源就会留出空间资源自增。 具体代码连接如下基于信号量的环形队列
http://www.sczhlp.com/news/168730/

相关文章:

  • 济南专业做网站龙岗网站建设公司官网
  • 坪山网站开发wordpress兼容手机吗
  • 网站主题选择天津网站建设方案报价
  • 搜索引擎作弊的网站有哪些长沙招聘信息
  • 松江 企业网站建设泉州手机网站建设
  • 自己做响应式网站难吗响应式网站 解决方案
  • 温州微网站公司新浪网页版入口
  • 网站 微信公众号 建设方案wordpress 图片加速
  • 视频网站建设审批摄影作品哪里看
  • jsp网站建设模板河南城乡建设网站
  • 营销型网站创建宁波免费seo排名优化
  • 什么网站都有漏洞网站建设培训网站
  • 二级网站怎样做jq效果较多的网站
  • 图书馆建设网站打不开群晖 搭建wordpress
  • 用php和mysql做网站建立网站需要花多少费用
  • 简单的网站建设找哪个公司wordpress 登录弹窗
  • 厦门网站seo哪家好福建网站建设有限公司
  • 嘉定网站建设wordpress分类标题nothing found
  • 即墨医院网站制作公司郑州制作网站公司
  • 宁波建设网站报价大型门户网站建设
  • 网站不能正常显示出现后台代码抖音代运营违法吗?
  • 圣弘建设股份有限公司网站免费自助建站网站一览自助建网站
  • 专门做瓷砖的网站网站信息可以
  • 网站建设报价方案模板鸿蒙系统ui设计规范
  • 网页设计首页子页网站 seo 优化建议
  • 网站开发招投标书wordpress电影站开发
  • 建站平台 discuz做网站的思路怎么写
  • 在元典公司做网站有合同吗无忧网站建设服务
  • 做网站做软件怎么赚钱国际公司
  • 聋哑工作设计做网站中国企业集成网电子商务