cpp
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 wakeup
while (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,线程库不会立即唤醒等待线程并让其竞争锁。
- 相反,线程库会将等待线程的 "等待状态" 直接转换为 "就绪状态",并在当前线程释放锁时无缝地将锁交给被唤醒的线程。
- 这种优化避免了被唤醒线程立即竞争锁失败并重新睡眠的情况,减少了上下文切换和调度开销。