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

网站卖链接百度访问量统计

网站卖链接,百度访问量统计,网站广告位投放,国内管理咨询公司线程安全 Linux线程互斥进程线程间的互斥相关背景概念互斥量mutex互斥量的接口互斥量实现原理探究 可重入VS线程安全概念常见的线程不安全的情况常见的线程安全的情况常见的不可重入的情况常见的可重入的情况可重入与线程安全联系可重入与线程安全区别 常见锁概念死锁死锁的四个… 线程安全 Linux线程互斥进程线程间的互斥相关背景概念互斥量mutex互斥量的接口互斥量实现原理探究 可重入VS线程安全概念常见的线程不安全的情况常见的线程安全的情况常见的不可重入的情况常见的可重入的情况可重入与线程安全联系可重入与线程安全区别 常见锁概念死锁死锁的四个必要条件避免死锁 Linux线程同步同步概念与竞态条件条件变量条件变量函数 Linux线程互斥 进程线程间的互斥相关背景概念 临界资源多线程执行流共享的资源就叫做临界资源临界区每个线程内部访问临界自娱的代码就叫做临界区互斥任何时刻互斥保证有且只有一个执行流进入临界区访问临界资源通常对临界资源起保护作用原子性不会被任何调度机制打断的操作该操作只有两态要么完成要么未完成。 临界资源和临界区 进程之间如果要进行通信我们需要先创建第三方资源让不同的进程看到同一份资源由于这份第三方资源可以由操作系统中的不同模块提供于是进程间通信的方式有很多种。进程间通信中的第三方资源就叫做临界资源访问第三方资源的代码就叫做临界区。 而多线程的大部分资源都是共享的线程之间进行通信不需要费那么大的劲去创建第三方资源。 例如我们在全局区定义一个count变量让新线程每隔一秒对该变量加一操作让主线程每隔一秒获取count变量的值进行打印 #include iostream #include cstdio #include unistd.h #include pthread.husing namespace std;int count 0;void* Routine(void* args) {while(true){count;sleep(1);}pthread_exit((void*)0); } int main() {pthread_t tid;pthread_create(tid, nullptr, Routine, nullptr);while(true){cout count: count endl;sleep(1);}pthread_join(tid, nullptr);return 0; }运行代码 此时我们相当于实现了主线程和新线程之间的通信其中全局变量count就叫做临界资源因为它被多个执行流共享而主线程中的printf和新线程中count就叫做临界区因为这些代码对临界资源进行了访问。 互斥和原子性 在多线程情况下如果这多个执行流都自顾自的对临界资源进行操作那么此时就可能导致数据不一致的问题。解决该问题的方案就叫做互斥互斥的作用就是保证在任何时候有且只有一个执行流进入临界区对临界资源进行访问。 原子性指的是不可被分割的操作该操作不会被任何调度机制打断该操作只有两态要么完成要么未完成。 下面我们模拟实现一个抢票系统我们将记录票的剩余张数的变量定义为全局变量主线程创建三个新线程让这三个新线程进行抢票当票被抢完后这四个线程自动退出 #include iostream #include cstdio #include unistd.h #include pthread.husing namespace std;int tickets 10000;void* getTickets(void* args) {(void*) args;while(true){if(tickets 0){usleep(1000);printf(%p: %d\n, pthread_self(), tickets);tickets--;}else{break;}}pthread_exit((void*)0); } int main() {pthread_t t1, t2, t3;pthread_create(t1, nullptr, getTickets, nullptr);pthread_create(t2, nullptr, getTickets, nullptr);pthread_create(t3, nullptr, getTickets, nullptr);pthread_join(t1, nullptr);pthread_join(t2, nullptr);pthread_join(t3, nullptr);return 0; }运行代码我们会发现出现了票数为负的情况显然这是不正常的 该代码中记录剩余票数的变量tickets就是临界资源因为它被多个执行流同时访问而判断tickets是否大于0、打印剩余票数以及tickets--这些代码就是临界区这些代码对临界资源进行了访问。 剩余票数出现负数的原因 if语句判断条件为真以后代码可以并发的切换到其他线程。usleep用于模拟漫长业务的过程在这个漫长的业务过程中可能有很多个线程会进入该代码段。ticket--操作本身就不是一个原子操作。 为什么ticket--不是原子操作 我们对一个变量进行--我们实际需要进行以下三个步骤 load将共享变量tickets从内存加载到寄存器中。update更新寄存器里面的值执行-1操作。store将新值从寄存器写回共享变量tickets的内存地址。 操作对应的汇编代码如下 当thread 1 刚把tickets的值读进CPU时就可能已经被切走了从CPU剥离下来并没有进行--操作假如我们此时thread 1的信息就是10000当他被切换走以后他的上下文信息就会被保存下来此时thread 1就处于挂起状态。 此时thread 2正好被调度了而我们的thread 1 刚把tickets的值读进CPU时就被切换走了还没有进行--操作此时thread 2也是从10000开始进行--操作假设OS分配给thread 2的时间比较长此时thread 2进程1000次--操作并且被切回内存。 此时thread 2切换走thread 1恢复回来操作系统会让thread 1上下文数据恢复继续执行刚刚的操作此时我们的tickets就又变为了10000进行一次--操作以后变为9999此时在切回内存。 在上述过程中thread1抢了1张票thread2抢了1000张票而此时剩余的票数却是9999也就相当于多出了1000张票。 因此对一个变量进行--操作并不是原子的虽然–tickets看起来就是一行代码但这行代码被编译器编译后本质上是三行汇编相反对一个变量进行也需要对应的三个步骤即操作也不是原子操作。 互斥量mutex 大部分情况线程使用的数据都是局部变量变量的地址空间在线程栈空间内这种情况变量归属单个线程其他线程无法获得这种变量。但有时候很多变量都需要在线程间共享这样的变量成为共享变量可以通过数据的共享完成线程之间的交互。多个线程并发的操作共享变量就会带来一些问题。 要解决上述抢票系统的问题需要做到三点 代码必须有互斥行为当代码进入临界区执行时不允许其他线程进入该临界区。如果多个线程同时要求执行临界区的代码并且此时临界区没有线程在执行那么只能允许一个线程进入该临界区。如果线程不在临界区中执行那么该线程不能阻止其他线程进入临界区。 要做到这三点本质上就是需要一把锁Linux上提供的这把锁叫互斥量。 互斥量的接口 初始化互斥量 初始化互斥量有两种方法 方法1静态分配 pthread_mutex_t mutex PTHREAD_MUTEX_INITIALIZER方法2动态分配 int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);参数 mutex要初始化的互斥量attrNULL 返回值说明 互斥量初始化成功返回0失败返回错误码 销毁互斥量 销毁互斥量的函数叫做pthread_mutex_destroy该函数的函数原型如下 int pthread_mutex_destroy(pthread_mutex_t *mutex); 参数说明 mutex需要销毁的互斥量。 返回值说明 互斥量销毁成功返回0失败返回错误码。 销毁互斥量需要注意 使用PTHREAD_MUTEX_INITIALIZER初始化的互斥量不需要销毁。不要销毁一个已经加锁的互斥量。已经销毁的互斥量要确保后面不会有线程再尝试加锁。 互斥量加锁 互斥量加锁的函数叫做pthread_mutex_lock该函数的函数原型如下 int pthread_mutex_lock(pthread_mutex_t *mutex); 参数说明 mutex需要加锁的互斥量。 返回值说明 互斥量加锁成功返回0失败返回错误码。 调用pthread_mutex_lock时可能会遇到以下情况 互斥量处于未锁状态该函数会将互斥量锁定同时返回成功。发起函数调用时其他线程已经锁定互斥量或者存在其他线程同时申请互斥量但没有竞争到互斥量那么pthread_mutex_lock调用会陷入阻塞执行流被挂起等待互斥量解锁。 互斥量解锁 互斥量解锁的函数叫做pthread_mutex_unlock该函数的函数原型如下 int pthread_mutex_unlock(pthread_mutex_t *mutex); 参数说明 mutex需要解锁的互斥量。 返回值说明 互斥量解锁成功返回0失败返回错误码。 我们在上述的抢票系统中引入互斥量每一个线程要进入临界区之前都必须先申请锁只有申请到锁的线程才可以进入临界区对临界资源进行访问并且当线程出临界区的时候需要释放锁这样才能让其余要进入临界区的线程继续竞争锁。 #include iostream #include cstdio #include cstring #include cassert #include unistd.h #include pthread.husing namespace std; #define THREAD_NUM 1000int tickets 10000;class ThreadData { public:ThreadData(const string name, pthread_mutex_t* mtx):tname(name),pmtx(mtx){}public:string tname;pthread_mutex_t* pmtx; };void *getTickets(void *args) {ThreadData* td (ThreadData*)args;while (true){int n pthread_mutex_lock(td-pmtx);assert(n0);if (tickets 0){usleep(1000);printf(%s: %d\n, td-tname.c_str(), tickets);tickets--;n pthread_mutex_unlock(td-pmtx);assert(n0);}else{n pthread_mutex_unlock(td-pmtx);assert(n0);break;}}delete td;pthread_exit((void *)0); } int main() {pthread_mutex_t mutex;pthread_mutex_init(mutex, nullptr);pthread_t t[THREAD_NUM];for(int i 0; i THREAD_NUM; i){string name thread;name to_string(i1);ThreadData* td new ThreadData(name, mutex);pthread_create(ti, nullptr, getTickets, (void*)td);}for(int i 0; i THREAD_NUM; i){pthread_join(t[i], nullptr);}pthread_mutex_destroy(mutex);return 0; } 运行代码此时在抢票过程中就不会出现票数剩余为负数的情况了。 在大部分情况下加锁本身都是有损于性能的事它让多执行流由并行执行变为了串行执行这几乎是不可避免的。我们应该在合适的位置进行加锁和解锁这样能尽可能减少加锁带来的性能开销成本。进行临界资源的保护是所有执行流都应该遵守的标准这时程序员在编码时需要注意的。 互斥量实现原理探究 加锁后的原子性体现在哪里 引入互斥量后当一个线程申请到锁进入临界区时在其他线程看来该线程只有两种状态要么没有申请锁要么锁已经释放了因为只有这两种状态对其他线程才是有意义的。 例如图中线程1进入临界区后在线程2、3、4看来线程1要么没有申请锁要么线程1已经将锁释放了因为只有这两种状态对线程2、3、4才是有意义的当线程2、3、4检测到其他状态时也就被阻塞了。 此时对于线程2、3、4而言它们就认为线程1的整个操作过程是原子的。 临界区内的线程可能进行线程切换吗 临界区内的线程完全可能进行线程切换但即便该线程被切走其他线程也无法进入临界区进行资源访问因为此时该线程是拿着锁被切走的锁没有被释放也就意味着其他线程无法申请到锁也就无法进入临界区进行资源访问了。 其他想进入该临界区进行资源访问的线程必须等该线程执行完临界区的代码并释放锁之后才能申请锁申请到锁之后才能进入临界区。 锁是否需要被保护 我们说被多个执行流共享的资源叫做临界资源访问临界资源的代码叫做临界区。所有的线程在进入临界区之前都必须竞争式的申请锁因此锁也是被多个执行流共享的资源也就是说锁本身就是临界资源。 既然锁是临界资源那么锁就必须被保护起来但锁本身就是用来保护临界资源的那锁又由谁来保护的呢 锁实际上是自己保护自己的我们只需要保证申请锁的过程是原子的那么锁就是安全的。 如何保证申请锁的过程是原子的 --和操作不是原子操作可能会导致数据不一致问题。为了实现互斥锁操作大多数体系结构都提供了swap或exchange指令该指令的作用就是把寄存器和内存单元的数据相交换。由于只有一条指令保证了原子性即使是多处理器平台访问内存的总线周期也有先后一个处理器上的交换指令执行时另一个处理器的交换指令只能等待总线周期。 下面我们来看看lock和unlock的伪代码 我们可以认为mutex的初始值为1al是计算机中的一个寄存器当线程申请锁时需要执行以下步骤 先将al寄存器中的值清0。该动作可以被多个线程同时执行因为每个线程都有自己的一组寄存器上下文信息执行该动作本质上是将自己的al寄存器清0。然后交换al寄存器和mutex中的值。xchgb是体系结构提供的交换指令该指令可以完成寄存器和内存单元之间数据的交换。最后判断al寄存器中的值是否大于0。若大于0则申请锁成功此时就可以进入临界区访问对应的临界资源否则申请锁失败需要被挂起等待直到锁被释放后再次竞争申请锁。 接下来我们来详细解释一下 假设我们有两个线程AB此时线程A需要进行操作内存中mutex的值为1线程申请锁时先将al寄存器中的值清0 当线程A完成第一步操作以后此时操作系统给线程A分配的时间到了线程A带着他的上下文数据就被切换走了此时线程B进程操作同样先将al寄存器中的值清0 然后线程B将al寄存器中的值与内存中mutex的值进行交换。 此时操作系统给线程B分配的时间刚好也到了就又切换回线程A线程A带着他的上下文数据就回来了继续进行刚才第二步操作交换数据此时al寄存器中的值也就变成了0但由于刚才mutex的值被线程B进行了交换此时mutex也为0两者交换以后还是0 我们就会发现线程A进入判断步骤以后al寄存器中的值此时不大于0就会挂起等待 此时OS发现线程A在等待就右切换回线程B完成刚才未完成操作return以后进行解锁操作将mutex置为1在将等待的线程A唤醒 如此也就可以保证申请锁过程的原子性了。 注意 在申请锁时本质上就是哪一个线程先执行了交换指令那么该线程就申请锁成功因为此时该线程的al寄存器中的值就是1了。而交换指令就只是一条汇编指令一个线程要么执行了交换指令要么没有执行交换指令所以申请锁的过程是原子的。在线程释放锁时没有将当前线程al寄存器中的值清0这不会造成影响因为每次线程在申请锁时都会先将自己al寄存器中的值清0再执行交换指令。CPU内的寄存器不是被所有的线程共享的每个线程都有自己的一组寄存器但内存中的数据是各个线程共享的。申请锁实际就是把内存中的mutex通过交换指令原子性的交换到自己的al寄存器中。 可重入VS线程安全 概念 线程安全 多个线程并发同一段代码时不会出现不同的结果。常见对全局变量或者静态变量进行操作并且没有锁保护的情况下会出现线程安全问题。 重入 同一个函数被不同的执行流调用当前一个流程还没有执行完就有其他的执行流再次进入我们称之为重入。一个函数在重入的情况下运行结果不会出现任何不同或者任何问题则该函数被称为可重入函数否则是不可重入函数。 注意 线程安全讨论的是线程执行代码时是否安全重入讨论的是函数被重入进入。 常见的线程不安全的情况 不保护共享变量的函数。函数状态随着被调用状态发生变化的函数。返回指向静态变量指针的函数。调用线程不安全函数的函数。 常见的线程安全的情况 每个线程对全局变量或者静态变量只有读取的权限而没有写入的权限一般来说这些线程是安全的。类或者接口对于线程来说都是原子操作。多个线程之间的切换不会导致该接口的执行结果存在二义性。 常见的不可重入的情况 调用了malloc/free函数因为malloc函数是用全局链表来管理堆的。调用了标准I/O库函数标准I/O可以的很多实现都是以不可重入的方式使用全局数据结构。可重入函数体内使用了静态的数据结构。 常见的可重入的情况 不使用全局变量或静态变量。不使用malloc或者new开辟出的空间。不调用不可重入函数。不返回静态或全局数据所有数据都由函数的调用者提供。使用本地数据或者通过制作全局数据的本地拷贝来保护全局数据。 可重入与线程安全联系 函数是可重入的那就是线程安全的。函数是不可重入的那就不能由多个线程使用有可能引发线程安全问题。如果一个函数中有全局变量那么这个函数既不是线程安全也不是可重入的。 可重入与线程安全区别 可重入函数是线程安全函数的一种。线程安全不一定是可重入的而可重入函数则一定是线程安全的。如果对临界资源的访问加上锁则这个函数是线程安全的但如果这个重入函数的锁还未释放则会产生死锁因此是不可重入的。 常见锁概念 死锁 死锁是指在一组进程中的各个进程均占有不会释放的资源但因互相申请被其他进程所占用不会释放的资源而处于的一种永久等待状态。 如下图所示线程A申请申请了锁1并不会进行释放线程B申请了锁2也不会进行释放此时线程A需要申请锁2线程B需要申请锁1显然是不可能成功的这就叫做死锁。 单执行流可能产生死锁吗 单执行流也有可能产生死锁如果某一执行流连续申请了两次锁那么此时该执行流就会被挂起。因为该执行流第一次申请锁的时候是申请成功的但第二次申请锁时因为该锁已经被申请过了于是申请失败导致被挂起直到该锁被释放时才会被唤醒但是这个锁本来就在自己手上自己现在处于被挂起的状态根本没有机会释放锁所以该执行流将永远不会被唤醒此时该执行流也就处于一种死锁的状态。 我们看下面这段代码 #include iostream #include cstdio #include pthread.husing namespace std;pthread_mutex_t mutex;void *Routine(void *args) {pthread_mutex_lock(mutex);while(true){pthread_mutex_lock(mutex);cout new thread runing..... endl;}pthread_exit((void*)0); }int main() {pthread_t tid;pthread_mutex_init(mutex, nullptr);pthread_create(tid, nullptr, Routine, nullptr);pthread_join(tid, nullptr);pthread_mutex_destroy(mutex);return 0; }运行代码此时该程序实际就处于一种被挂起的状态 用ps命令查看该进程时可以看到该进程当前的状态是Sl其中的l实际上就是lock的意思表示该进程当前处于一种死锁的状态。 什么叫做阻塞 进程运行时是被CPU调度的换句话说进程在调度时是需要用到CPU资源的每个CPU都有一个运行等待队列runqueueCPU在运行时就是从该队列中获取进程进行调度的。 在运行等待队列中的进程本质上就是在等待CPU资源实际上不止是等待CPU资源如此等待其他资源也是如此比如锁的资源、磁盘的资源、网卡的资源等等它们都有各自对应的资源等待队列。 例如当某一个进程在被CPU调度时该进程需要用到锁的资源但是此时锁的资源正在被其他进程使用 那么此时该进程的状态就会由R状态变为某种阻塞状态比如S状态。并且该进程会被移出运行等待队列被链接到等待锁的资源的资源等待队列而CPU则继续调度运行等待队列中的下一个进程。此后若还有进程需要用到这一个锁的资源那么这些进程也都会被移出运行等待队列依次链接到这个锁的资源等待队列当中。直到使用锁的进程已经使用完毕也就是锁的资源已经就绪此时就会从锁的资源等待队列中唤醒一个进程将该进程的状态由S状态改为R状态并将其重新链接到运行等待队列等到CPU再次调度该进程时该进程就可以使用到锁的资源了。 总结下来就是 站在操作系统的角度进程等待某种资源就是将当前进程的task_struct放入对应的等待队列这种情况可以称之为当前进程被挂起等待了。站在用户角度当进程等待某种资源时用户看到的就是自己的进程卡住不动了我们一般称之为应用阻塞了。这里所说的资源可以是硬件资源也可以是软件资源锁本质就是一种软件资源当我们申请锁时锁当前可能并没有就绪可能正在被其他线程所占用此时当其他线程再来申请锁时就会被放到这个锁的资源等待队列当中。 死锁的四个必要条件 互斥条件 一个资源每次只能被一个执行流使用。请求与保持条件 一个执行流因请求资源而阻塞时对已获得的资源保持不放。不剥夺条件 一个执行流已获得的资源在未使用完之前不能强行剥夺。循环等待条件 若干执行流之间形成一种头尾相接的循环等待资源的关系。 注意 这是死锁的四个必要条件也就是说只有同时满足了这四个条件才可能产生死锁。 避免死锁 破坏死锁的四个必要条件。加锁顺序一致。避免锁未释放的场景。资源一次性分配。 除此之外还有一些避免死锁的算法比如死锁检测算法和银行家算法。 Linux线程同步 同步概念与竞态条件 同步 在保证数据安全的前提下让线程能够按照某种特定的顺序访问临界资源从而有效避免饥饿问题这就叫做同步。竞态条件 因为时序问题而导致程序异常我们称之为竞态条件。 首先我们得理解单纯的加锁并不存在什么什么问题但是由于各个线程之间的竞争能力有所差异可能就会存在某一个线程频繁地申请锁和释放锁假设此时我们有一个线程A和B线程A需要写入数据线程B需要读取数据此时线程A的能力强一点他就会一直申请锁和释放锁。直到临界区数据被写满此时线程B一直就处于阻塞状态所以单纯的加锁不存在什么问题但是对于效率来说又比较影响。 那么如何解决这个问题呢 我们增加一个规律当一个线程释放锁后这个线程不能立马再次申请锁该线程必须排到这个锁的资源等待队列的最后增加这个规则之后下一个获取到锁的资源的线程就一定是在资源等待队列首部的线程如果有十个线程此时我们就能够让这十个线程按照某种次序进行临界资源的访问这就叫做同步引入同步后该问题就能很好的解决。 条件变量 条件变量是利用线程间共享的全局变量进行同步的一种机制条件变量是用来描述某种资源是否就绪的一种数据化描述。 条件变量主要包括两个动作 一个线程等待条件变量的条件成立而被挂起。另一个线程使条件成立后唤醒等待的线程。 条件变量通常需要配合互斥锁一起使用。 条件变量函数 初始化 int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr); 参数说明 cond需要初始化的条件变量。attr初始化条件变量的属性一般设置为NULL即可。 返回值说明 条件变量初始化成功返回0失败返回错误码。 调用pthread_cond_init函数初始化条件变量叫做动态分配除此之外我们还可以用下面这种方式初始化条件变量该方式叫做静态分配 pthread_cond_t cond PTHREAD_COND_INITIALIZER; 销毁 销毁条件变量的函数叫做pthread_cond_destroy该函数的函数原型如下 int pthread_cond_destroy(pthread_cond_t *cond); 参数说明 cond需要销毁的条件变量。 返回值说明 条件变量销毁成功返回0失败返回错误码。 销毁条件变量需要注意 使用PTHREAD_COND_INITIALIZER初始化的条件变量不需要销毁。 等待条件满足 等待条件变量满足的函数叫做pthread_cond_wait该函数的函数原型如下 int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex); 参数说明 cond需要等待的条件变量。mutex当前线程所处临界区对应的互斥锁。 返回值说明 函数调用成功返回0失败返回错误码。 唤醒等待 唤醒等待的函数有以下两个 int pthread_cond_broadcast(pthread_cond_t *cond); int pthread_cond_signal(pthread_cond_t *cond); 区别 pthread_cond_signal函数用于唤醒等待队列中首个线程。pthread_cond_broadcast函数用于唤醒等待队列中的全部线程。 参数说明 cond唤醒在cond条件变量下等待的线程。 返回值说明 函数调用成功返回0失败返回错误码。 下面我们用主线程创建四个新线程让主线程控制这四个新线程活动。这四个新线程创建后都在条件变量下进行等待直到主线程检测到键盘有输入时才唤醒一个等待线程如此进行下去。 #include iostream #include unistd.h #include cstring#define TNUM 4 typedef void (*func_t)(const std::string name, pthread_mutex_t *pmutex, pthread_cond_t *pcond); volatile bool quit false;class ThreadData { public:ThreadData(const std::string name, func_t funcs, pthread_mutex_t *pmutex, pthread_cond_t *pcond): _name(name), _funcs(funcs), _mutex(pmutex), _cond(pcond){}public:std::string _name;func_t _funcs;pthread_mutex_t *_mutex;pthread_cond_t *_cond; };void func1(const std::string name, pthread_mutex_t *pmutex, pthread_cond_t *pcond) {while (!quit){//一定要在加锁和解锁之间进行waitpthread_mutex_lock(pmutex);// if(临界资源是否就绪-- 否)pthread_cond_wait(pcond, pmutex);//默认该线程在执行的时候wait代码被执行当前线程会被立即被阻塞std::cout name runing-- 下载 std::endl;pthread_mutex_unlock(pmutex);} }void func2(const std::string name, pthread_mutex_t *pmutex, pthread_cond_t *pcond) {while (!quit){pthread_mutex_lock(pmutex);pthread_cond_wait(pcond, pmutex);//默认该线程在执行的时候wait代码被执行当前线程会被立即被阻塞std::cout name runing-- 播放 std::endl;pthread_mutex_unlock(pmutex);} }void func3(const std::string name, pthread_mutex_t *pmutex, pthread_cond_t *pcond) {while (!quit){pthread_mutex_lock(pmutex);pthread_cond_wait(pcond, pmutex);//默认该线程在执行的时候wait代码被执行当前线程会被立即被阻塞std::cout name runing-- 刷新 std::endl;pthread_mutex_unlock(pmutex);} }void func4(const std::string name, pthread_mutex_t *pmutex, pthread_cond_t *pcond) {while (!quit){pthread_mutex_lock(pmutex);pthread_cond_wait(pcond, pmutex);//默认该线程在执行的时候wait代码被执行当前线程会被立即被阻塞std::cout name runing-- 扫描用户信息 std::endl;pthread_mutex_unlock(pmutex);} }void *Entry(void *args) {ThreadData *td (ThreadData *)args;td-_funcs(td-_name, td-_mutex, td-_cond);delete td;pthread_exit((void*)0); } int main() {// 创建锁pthread_mutex_t mutex;pthread_cond_t cond;// 初始化锁pthread_mutex_init(mutex, nullptr);pthread_cond_init(cond, nullptr);// 创建线程pthread_t tid[TNUM];func_t funcs[TNUM] {func1, func2, func3, func4};for (int i 0; i TNUM; i){std::string name thread;name std::to_string(i 1);ThreadData *td new ThreadData(name, funcs[i], mutex, cond);pthread_create(tid i, nullptr, Entry, (void *)td);}int cnt 10;while(cnt){std::cout resume thread run code .... cnt-- std::endl;pthread_cond_signal(cond);sleep(1);}std::cout ctrl done std::endl;quit true;pthread_cond_broadcast(cond);//主线程进程等待for(int i 0; i TNUM; i){pthread_join(tid[i], nullptr);std::cout thread[ tid[i] ] quit!!! std::endl;}pthread_mutex_destroy(mutex);pthread_cond_destroy(cond);return 0; }此时我们会发现唤醒这4个线程时具有明显的顺序性根本原因是当这若干个线程启动时默认都会在该条件变量下去等待而我们每次都唤醒的是在当前条件变量下等待的头部线程当该线程执行完打印操作后会继续排到等待队列的尾部进行wait所以我们能够看到一个周转的现象。 如果我们想每次唤醒都将在该条件变量下等待的所有线程进行唤醒可以将代码中的pthread_cond_signal函数改为pthread_cond_broadcast函数。 此时我们每一次唤醒都会将所有在该条件变量下等待的线程进行唤醒也就是每次都将这4个线程唤醒。
http://www.zqtcl.cn/news/530483/

相关文章:

  • 安徽鸿顺鑫城建设集团网站设计外贸英文网站
  • 浦口区教育局网站集约化建设南京淄博网站建设方案
  • 学校网站建设的风险分析wordpress侧边栏显示单个分类列表
  • php网站安装图解帮别人建设网站多少利润
  • vs做的网站如何二手优品哪个网站做
  • 建设银行内部网站6建筑学院官网
  • 大学生做微商网站金华seo扣费
  • 以前老网站邯郸推广网络宣传哪家好
  • 网站建设技术网站建设奉节网站建设公司
  • 动漫视频网站模板动漫制作专业什么电脑最适合
  • 合网站建设注册一个500万的公司需要多少钱
  • 《网站推广策划》wordpress 写博客
  • 网站开发工程师面试问哪些问题免费下载软件商店安装
  • 建网站公司要钱吗公司制作网站跟企业文化的关系
  • 网站改版对用户的影响网站评论怎么做的
  • 许昌市做网站公司汉狮价格装修案例图片 效果图
  • 设计主题网站化肥厂的网站摸板
  • 做羊水亲子鉴定网站网络推广是啥
  • 怎样解析网站域名用哪个网站做首页比较好
  • 设计网站页面设计wordpress样式错乱
  • 静态网页模板免费网站wordpress悬浮按钮
  • 怎么制作学校网站大淘客网站代码
  • 如何做好一个网站wordpress 修改邮箱设置
  • 网站项目方案生态建设研究所网站
  • 用织梦做视频网站wordpress文章不能分段
  • 彩票网站开发. 极云邮箱类网站模板
  • 网站代运营协议网站 文件服务器
  • 专业网站设计公司有哪些绿色营销案例100例
  • 网站建设买了域名山东省作风建设网站
  • 留学中介网站建设方案设计企业品牌商标