大学生网站设计论文3000字,威远移动网站建设,网站开发招标,出口网站平台互斥器和条件变量的区别#xff1a;互斥器具有加锁原语#xff0c;用来进行排他性的访问共享数据#xff0c;而条件变量具有等待原语#xff0c;用于等待某个事件的发生。 等待条件变量的正确姿势#xff1a; void wait() {mutex.lock()while (wait_flag false) {conditi…互斥器和条件变量的区别互斥器具有加锁原语用来进行排他性的访问共享数据而条件变量具有等待原语用于等待某个事件的发生。 等待条件变量的正确姿势 void wait() {mutex.lock()while (wait_flag false) {condition.wait()}do_something();mutex.unlock();
} 1必须使用while循环来等待条件变为真即醒来之后要立马再判断一次条件是否成立再决定是否需要继续等待 因为很有可能条件并不为真但是线程却被各种奇怪的中断或者pthread_cond_broadcast这样的东西给唤醒了 2至于condition.wait()的作用是提供一个原子操作 进入condition.wait() 时将 wait 和 mutex.unlock 两步变成一个原子操作确保在线程进入 wait 之前不会释放锁也就是保证了在进入 wait 之前没有人能够改动 wait_flag (所有对 wait_flag的操作都要加锁否则条件变量没有意义) 因为pthread_cond_signal这类函数只会唤醒已经在等待队列里的线程如果这两步不是原子操作那么在释放锁之后进入等待队列之前的这段时间里有人调用了cond_signal之后也不会被唤醒 触发条件的正确姿势 void notify() {mutex.lock();push_resource();condition.signal();mutex.unlock();
}void notifyAll() {mutex.lock();change_status();condition.broadcast();mutex.unlock();
} 通常在资源可用时使用signal在改变状态时使用broadcast。 这里特别说明一下网上流传的阻塞队列 (为简单起见这里假设队列最大长度为无限长) 的实现有个错误 void push(e) {mutex.lock();queue.push(e);if (queue.size() 1) { //错误应该把if条件去掉cond.notify(); //或者不去掉if改成notifyAll}mutex.unlock();
} 因为pthread_cond_signal只是唤醒线程到就绪队列至于这个线程能不能抢到锁以及抢到锁之后能不能保证立马被调度并从队列里取元素就不保证了。 所以会出现这种情况有多个饥饿的线程在等待队列变成非空这个时候有一个线程放进去一个元素之后唤醒了一个等待线程到就绪队列但是他却没有获取到锁 这个时候其他的线程往队列里push的时候由于消费者线程没有来得及取元素所以队列元素的总量大于1所以并不触发signal。。。 这就导致在下次队列变为空之前可能只有一个消费者线程会被唤醒。 使用notifyAll可以避免这个问题但是代价是虚假唤醒(据说对这种情况会优化这里不讨论。。恩因为我特么也是不懂啊记住正确的做法不就好了么) 所以对于有多个消费者线程的阻塞队列正确的写法是每次push都进行一次notify()而不是队列为空时才notify ---------------------------------------------------------------------------------------------------------------------------------- pthread_cond_wait总和一个互斥锁结合使用。在调用pthread_cond_wait前要先获取锁。pthread_cond_wait函数执行时先自动释放指定的锁然后等待条件变量的变化。在函数调用返回之前自动将指定的互斥量重新锁住。 int pthread_cond_signal(pthread_cond_t * cond); pthread_cond_signal通过条件变量cond发送消息若多个消息在等待它只唤醒一个。pthread_cond_broadcast可以唤醒所有。调用pthread_cond_signal后要立刻释放互斥锁因为pthread_cond_wait的最后一步是要将指定的互斥量重新锁住如果pthread_cond_signal之后没有释放互斥锁pthread_cond_wait仍然要阻塞。 无论哪种等待方式都必须和一个互斥锁配合以防止多个线程同时请求pthread_cond_wait()或pthread_cond_timedwait()下同的竞争条件Race Condition。mutex互斥锁必须是普通锁PTHREAD_MUTEX_TIMED_NP或者适应锁 PTHREAD_MUTEX_ADAPTIVE_NP且在调用pthread_cond_wait()前必须由本线程加锁 pthread_mutex_lock()而在更新条件等待队列以前mutex保持锁定状态并在线程挂起进入等待前解锁。在条件满足从而离开 pthread_cond_wait()之前mutex将被重新加锁以与进入pthread_cond_wait()前的加锁动作对应。 激发条件有两种形式pthread_cond_signal()激活一个等待该条件的线程存在多个等待线程时按入队顺序激活其中一个而pthread_cond_broadcast()则激活所有等待线程。 下面是另一处说明给出了函数运行全过程。 为什么在唤醒线程后要重新mutex加锁
了解 pthread_cond_wait() 的作用非常重要 -- 它是 POSIX 线程信号发送系统的核心也是最难以理解的部分。首先让我们考虑以下情况线程为查看已链接列表而锁定了互斥对象然而该列表恰巧是空的。这一特定线程什么也干不了 -- 其设计意图是从列表中除去节点但是现在却没有节点。因此它只能锁定互斥对象时线程将调用 pthread_cond_wait(mycond,mymutex)。pthread_cond_wait() 调用相当复杂因此我们每次只执行它的一个操作。pthread_cond_wait() 所做的
第一件事
就是同时对互斥对象解锁于是其它线程可以修改已链接列表并等待条件 mycond 发生这样当 pthread_cond_wait() 接收到另一个线程的“信号”时它将苏醒。现在互斥对象已被解锁其它线程可以访问和修改已链接列表可能还会添加项。 【
要求解锁并阻塞是一个原子操作
】此时pthread_cond_wait() 调用还未返回。对互斥对象解锁会立即发生但等待条件 mycond 通常是一个阻塞操作这意味着线程将睡眠在它苏醒之前不会消耗 CPU 周期。这正是我们期待发生的情况。线程将
一直睡眠直到特定条件发生
在这期间不会发生任何浪费 CPU 时间的繁忙查询。从线程的角度来看它只是在等待 pthread_cond_wait() 调用返回。现在继续说明假设另一个线程称作“2 号线程”锁定了 mymutex 并对已链接列表添加了一项。在对互斥对象解锁之后2 号线程会立即调用函数 pthread_cond_broadcast(mycond)。此操作之后2 号线程将使所有等待 mycond 条件变量的线程立即苏醒。这意味着第一个线程仍处于 pthread_cond_wait() 调用中现在将
苏醒
。现在看一下第一个线程发生了什么。您可能会认为在 2 号线程调用 pthread_cond_broadcast(mymutex) 之后1 号线程的 pthread_cond_wait() 会立即返回。不是那样实际上pthread_cond_wait() 将执行最后一个操作
重新锁定 mymutex
。一旦 pthread_cond_wait() 锁定了互斥对象那么它将返回并允许 1 号线程继续执行。那时它可以马上检查列表查看它所感兴趣的更改。来看一个例子你是否能理解呢?In Thread1: pthread_mutex_lock(m_mutex); pthread_cond_wait(m_cond,m_mutex); pthread_mutex_unlock(m_mutex); In Thread2: pthread_mutex_lock(m_mutex); pthread_cond_signal(m_cond); pthread_mutex_unlock(m_mutex); 为什么要与pthread_mutex 一起使用呢 这是为了应对 线程1在调用pthread_cond_wait()但线程1还没有进入wait cond的状态的时候此时线程2调用了 cond_singal 的情况。 如果不用mutex锁的话这个cond_singal就丢失了。加了锁的情况是线程2必须等到 mutex 被释放也就是 pthread_cod_wait() 释放锁并进入wait_cond状态 此时线程2上锁 的时候才能调用cond_singal. pthread_cond_signal即可以放在pthread_mutex_lock和pthread_mutex_unlock之间也可以放在pthread_mutex_lock和pthread_mutex_unlock之后但是各有有缺点。 之间 pthread_mutex_lock xxxxxxx pthread_cond_signal pthread_mutex_unlock 缺点在某下线程的实现中会造成等待线程从内核中唤醒由于cond_signal)然后又回到内核空间因为cond_wait返回后会有原子加锁的 行为所以一来一回会有性能的问题。但是在LinuxThreads或者NPTL里面就不会有这个问题因为在Linux 线程中有两个队列分别是cond_wait队列和mutex_lock队列 cond_signal只是让线程从cond_wait队列移到mutex_lock队列而不用返回到用户空间不会有性能的损耗。 所以在Linux中推荐使用这种模式。 之后 pthread_mutex_lock xxxxxxx pthread_mutex_unlock pthread_cond_signal 优点不会出现之前说的那个潜在的性能损耗因为在signal之前就已经释放锁了 缺点如果unlock和signal之前有个低优先级的线程正在mutex上等待的话那么这个低优先级的线程就会抢占高优先级的线程cond_wait的线程)而这在上面的放中间的模式下是不会出现的。