公司网站策划书,长沙网站开发哪家好,oa软件怎么使用,电子商务网站搜索引擎设计目录
一、互斥
1.互斥的概念
2.互斥锁接口
3.线程加锁解锁本质
4.死锁
二、同步
1.同步的概念
2.条件变量
3.条件变量接口 一、互斥
1.互斥的概念 互斥指的是任何时刻#xff0c;互斥保证有且只有一个执行流进入临界区#xff0c;进行临界资源的访问#xff0c;通…目录
一、互斥
1.互斥的概念
2.互斥锁接口
3.线程加锁解锁本质
4.死锁
二、同步
1.同步的概念
2.条件变量
3.条件变量接口 一、互斥
1.互斥的概念 互斥指的是任何时刻互斥保证有且只有一个执行流进入临界区进行临界资源的访问通常对于临界资源起到保护作用的一种机制。 在多线程下访问全局变量的话一个线程的修改会影响到其他线程如果说一些操作不是原子的话会有数据不一致的问题。例如多线程下抢电影票的情况本来有count100张票但是最后可能卖出去100多张是因为count--操作不是原子的。 当执行count--操作的时候转化为汇编操作有3步第一步是将count的值读取出来放入CPU寄存器中第二步执行--操作第三步将count的值覆盖会物理内存的位置改变count在物理内存中的值。但是如果说在第二步执行完之后该线程的时间片到了就会从CPU上剥离下来本来取出来的是10在CPU中已经修改为9了但是没有写回到内存中所以内存的count值还是10那么别的线程就会读取到count值是10之后在CPU中在进行操作并写回物理内存的值也为9。等到第一个修改为9的进程再次回到CPU中执行时会执行第三步操作将9有一次的写回到了物理内存当中。此时count的值是多少都不知道可能已经减为0了但是又被写回了9所以说不是原子的操作在多线程下会有很大的问题。 又或者是多线程下一个变量充当if判断条件的时候可能刚读取到该变量时间片就到了没有来得及进行判断就被剥离了下来其他的线程继续操作最终把变量改为了不满足if判断条件了本来该线程应该判断后退出的但是该线程已经取出了该条件变量是最开始的值所以还是会判断符合条件执行if内部代码所以说也会出错的。 数据在内存是被多线程共享的但是一旦读取到寄存器当中就变成了线程的上下文数据就属于私有了。所以互斥就是为了解决上述的数据不一致问题就是同一时间只让一个线程进行访问该资源。 2.互斥锁接口 上述的问题可以使用互斥锁来解决锁是用来保护临界区在同一时间只能由一个进程进行访问的。锁相当于一个资源多线程去竞争该资源但是只有一个线程可以获取到该资源其他线程就会阻塞在锁的位置等待锁资源获取到锁资源的线程才可以去访问临界区的资源。当线程执行完临界区的操作之后释放锁其他线程才可以继续竞争锁资源。
全局锁 pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER; 创建局部锁 int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *attr); 第一个参数是要初始化的锁对象第二个参数是设置锁的属性一般设置为nullptr即可。
销毁局部锁 int pthread_mutex_destroy(pthread_mutex_t *mutex); 加锁和解锁 int pthread_mutex_lock(pthread_mutex_t *mutex); int pthread_mutex_unlock(pthread_mutex_t *mutex); int pthread_mutex_trylock(pthread_mutex_t *mutex); 对于pthread_mutex_lock加锁是阻塞式的申请锁资源如果没有申请到的话则会阻塞等待锁资源而pthread_mutex_trylock则是尝试申请锁资源也就是非阻塞式的申请如果有的话就申请到了没有的话就不申请了直接返回。 3.线程加锁解锁本质 多线程去申请锁资源锁保证了线程之间的互斥但是锁也是共享资源啊谁来保证锁的安全性呢所以要和信号量一样对于申请锁的操作设置为原子的。该原子的操作是基于硬件指令的支持比如说比较并交换的CAS等指令就是一步执行的操作操作。把申请锁的操作转换为原子的汇编指令就保证了申请锁的原子性。
pthread_mutex_lock()接口的加锁实现 lock movb %0, %al xchgb %al, mutex if(al寄存器 0) return 0; else 挂起等待 go to lock; 首先将al寄存器的内容置为0之后xchgb交换al寄存器和mutex标记位中的值当有锁资源的时候mutex的标记位是1没有的时候是0。xchagb就是一种原子性的操作指令也就是swap操作。 交换之后判断是否申请到了锁资源也就是判断al寄存器交换过来的数据是不是1如果是1的话证明获取了锁资源就可以执行访问临界区的代码了如果没有获取到那么就会进入else阻塞的等待如果有锁资源了会跳转到lock开头重复上述动作进行申请锁资源。 当有一批线程申请该锁资源的时候第一个进行swap交换的线程会将mutex的标记位交换位0而1则是进入了该线程的al寄存器中判断al寄存器大于0跳出循环执行临界区代码。此时锁的标记位就是0了其他线程来了之后再怎么交换al寄存器中的值都是0所以都需要进行阻塞等待。 当线程调度切换的时候也不会有影响其他线程来的时候还是要阻塞等待因为标志位1被申请到锁资源的线程通过切换上下文数据而带走了只有等到该线程重新被CPU调度执行完临界区代码将标志位1交换会mutex别的线程才能交换到标志位为1才能让al寄存器大于0从而获得到锁资源返回执行临界区代码。 也就是说锁资源的标志位就相当于一把钥匙也只要一把钥匙那么一个人拿走该钥匙之后不管什么情况当这个人没用完之前没把钥匙换回来之前其他人都打不开这个门都需要在门口进行等待。
pthread_mutex_unlock()接口的解锁实现 unlock: movb $1, mutex 唤醒阻塞等待的进程 return 0; 解锁的操作就是将标志位的1交换会锁资源中然后唤醒等待的线程去竞争锁资源。 4.死锁 死锁指的是一组线程中各个线程均占有不会被释放掉的资源但又因相互申请被其他线程锁占用不会释放的资源而处于一种持久等待的状态称之为死锁。例如下面的图所示有两个资源A和B1号线程占有A资源2号线程占有B资源而两者又互相想要对方的资源所以都在互相等待。 一个线程也可以产生死锁可以让一个进程对同一个锁资源重复申请两次就变成了拥有了该资源还在申请该资源的情况就变为了死锁。 那么死锁如何避免呢首先死锁的产生拥有四个条件互斥条件、请求与保持条件、不剥夺条件、循环等待条件。请求与保持指的是一个执行流因请求资源而阻塞的时候对以获得的资源保持不放并且还在一直申请资源。循环等待指的是若干个执行流之间形成了一种头尾详解的循环等待资源的关系。 解决死锁的方式就是破坏其中一个条件即可。例如不适用锁在申请锁资源不成功的时候把自己拥有的资源释放掉强行将别人的资源抢占过来按照同样的顺序申请锁资源就是对于多个资源的申请做一个先后顺序先有A资源才可以申请B资源而不是所有资源都可以无条件的申请。 二、同步
1.同步的概念 同步指的是在临界资源使用安全的情况下让多线程的执行具有一定的顺序性能够较为充分的利用资源。对于竞争锁资源谁竞争到我们是无法控制的但是我们可以通过一些方式来规定谁可以去竞争锁资源来实现线程访问临界资源具有一定的顺序性。 2.条件变量 条件变量是一种用于线程同步的机制。它主要用于让线程在某个条件满足之前等待当条件满足时线程可以被唤醒并继续执行。在多线程编程环境中条件变量提供了一种高效的方式来协调线程之间的执行顺序和资源访问。通常与互斥锁配合使用。互斥锁用于保护共享资源的访问而条件变量用于线程的等待和唤醒操作。 简单可以理解为他内部有一个标记位和一个线程等待队列当一个线程申请到锁资源的时候会访问临界区的代码此时我们在临界区代码中判断条件变量是否就绪如果说就绪的话条件变量的标记位为1就可以让申请到锁的进行继续访问临界区资源如果说没有准备就绪的话就把该线程链入到线程等待队列中同时释放锁资源。当条件准备就绪之后会唤醒等待队列的一个或多个进程继续竞争锁资源竞争到锁资源的线程就可以访问临界区资源了。 下面举一个场景有两个线程和一个缓冲区临界资源一个线程为写入线程一个线程为读取线程。那么显而易见需要先写入后读取那么这就是线程之间访问临界资源的顺序也就是线程的同步策略。那么如何实现呢用一把锁和一个条件变量去实现该条件变量关联的条件就是缓冲区中有数据。当读取进程申请到锁的时候会进入条件变量的判断因为条件变量没有就绪所以会就读写进程链入到线程等待队列中释放锁资源。那么写入线程就会竞争到锁资源进行写入写入之后会调用条件变量就绪接口并唤醒等待该条件变量的等待队列中的线程之后释放锁资源。那么读取线程就可以继续参与锁的竞争了当获取到锁资源后就可以读取临界区中的资源数据了。这就是使用条件变量和锁实现的线程同步。 3.条件变量接口
全局条件变量 pthread_cond_t cond PTHREAD_COND_INITIALIZER; 条件变量创建 int pthread_cond_init(pthread_cont_t* cond, const pthread_condattr_t* attr); 条件变量销毁 int pthread_cond_destroy(pthread_cond_t* cond); 等待条件变量 int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* mutex); 唤醒等待队列中的线程 int pthread_cond_signal(pthread_cond_t* cond); //唤醒队列头部的一个线程 int pthread_cond_broadcast(pthread_cond_t* cond); //唤醒所有线程 接口的使用代码
#include iostream
#include pthread.h
#include unistd.h//全局锁
pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER;
//全局条件变量
pthread_cond_t cond PTHREAD_COND_INITIALIZER;void* handler(void* arg)
{while(true){//申请锁资源pthread_mutex_lock(mutex);//等待条件变量就绪pthread_cond_wait(cond, mutex);//临界区std::cout pthread_self() -thread entry critical section std::endl;//释放锁资源pthread_mutex_unlock(mutex);}
}int main()
{//创建线程for(int i 0; i 5; i){pthread_t tid;pthread_create(tid, nullptr, handler, nullptr);//线程分离--无需等待pthread_detach(tid);}//反复让条件变量就绪唤醒一个线程while(true){pthread_cond_signal(cond);sleep(1);}return 0;
} 对于在条件变量等待队列被唤醒的线程也要重新参与锁的竞争但是并非是跳到申请锁的那行代码而是说该pthreat_cont_wait函数内部也是有申请锁的代码的。