C/C++ Wait Morphing锁内通知 锁外通知
template<typename T>
class BlockingQueue : noncopyable
{public:// 使用deque的原因:前插,尾插为O(1)消息队列尾巴放,前面拿// 为什么不list? // 因为deque有良好的缓存性,cache缓存,list 地址不同using queue_type = std::deque<T>;BlockingQueue(): mutex_(),notEmpty_(mutex_),queue_(){}//实现左值右值void put(const T& x){MutexLockGuard lock(mutex_);queue_.push_back(x); //尾插notEmpty_.notify(); // wait morphing saves us// http://www.domaigne.com/blog/computing/condvars-signal-with-mutex-locked-or-not/}void put(T&& x){MutexLockGuard lock(mutex_);queue_.push_back(std::move(x));notEmpty_.notify();}T take(){MutexLockGuard lock(mutex_);// always use a while-loop, due to spurious wakeupwhile (queue_.empty()){notEmpty_.wait();}assert(!queue_.empty());T front(std::move(queue_.front()));queue_.pop_front();return front;}queue_type drain(){std::deque<T> queue;{MutexLockGuard lock(mutex_);queue = std::move(queue_);assert(queue_.empty());}return queue;}size_t size() const{MutexLockGuard lock(mutex_);return queue_.size();}private:mutable MutexLock mutex_;Condition notEmpty_ GUARDED_BY(mutex_);queue_type queue_ GUARDED_BY(mutex_);
}; // __attribute__ ((aligned (64)));} // namespace muduo#endif // MUDUO_BASE_BLOCKINGQUEUE_H
锁内通知(notify with mutex locked):通知时当前线程持有锁,可能导致被唤醒的线程立即尝试获取锁,但因为锁还未释放,唤醒的线程会立即重新睡眠(spurious wakeup 或调度开销)。锁外通知(notify without mutex locked):通知时锁已释放,可能导致信号丢失(lost signal),即通知发生在等待线程检查条件之前。
- 锁内通知(notify with mutex locked):通知时当前线程持有锁,可能导致被唤醒的线程立即尝试获取锁,但因为锁还未释放,唤醒的线程会立即重新睡眠(spurious wakeup 或调度开销)。
- 锁外通知(notify without mutex locked):通知时锁已释放,可能导致信号丢失(lost signal),即通知发生在等待线程检查条件之前。
“Wait Morphing” 是什么?
当一个线程在持有锁时调用 notify,线程库不会立即唤醒等待线程并让其竞争锁。相反,线程库会将等待线程的 “等待状态” 直接转换为 “就绪状态”,并在当前线程释放锁时无缝地将锁交给被唤醒的线程。这种优化避免了被唤醒线程立即竞争锁失败并重新睡眠的情况,减少了上下文切换和调度开销。
- 当一个线程在持有锁时调用 notify,线程库不会立即唤醒等待线程并让其竞争锁。
- 相反,线程库会将等待线程的 “等待状态” 直接转换为 “就绪状态”,并在当前线程释放锁时无缝地将锁交给被唤醒的线程。
- 这种优化避免了被唤醒线程立即竞争锁失败并重新睡眠的情况,减少了上下文切换和调度开销。