双缓冲安全队列(DoubleCacheQueue)与自旋锁(SpinMutex)

论坛 期权论坛 脚本     
匿名技术用户   2020-12-27 00:30   11   0

原文转自:http://www.tanjp.com/archives/144 (即时修正和更新)

双缓冲安全队列(DoubleCacheQueue)

双缓冲区,故名思义就是要有两个缓冲区(简称A和B)。这两缓冲区,总是一个用于生产者,另一个用于消费者。当缓冲区触发某个条件时,进行一次切换(先前被生产者写入的转为消费者取出,先前消费者取出的转为生产者写入)。而这个触发的条件,一般是以快速消费完为主导(避免内存堆积太多数据),所以一般当消费缓冲区为空时,就触发交换。由于生产者和消费者不会同时操作同一个缓冲区(不发生冲突),所以就不需要在读写每一个数据单元的时候都进行同步/互斥操作。

PS: 多线程安全,当生产或消费的速度不平衡时,交换次数较少,能很明显提高性能

部分代码实现:

template<typename tpType>
class DoubleCacheQueue
{
     typedef std::queue< tpType > Queue;
     typedef spin_mutex Mutex;
public:
     explicit DoubleCacheQueue()
      : mn_count(0)
      , mp_push_queue(0)
  , mp_pop_queue(0)
  , mc_queue_A()
  , mc_queue_B() 
 { 
  mp_push_queue = &mc_queue_A;
  mp_pop_queue = &mc_queue_B;
 }
 bool push(const tpType & po_val)
 {
  std::lock_guard<Mutex> lock(mo_mutex_push);
  mp_push_queue->push(po_val);
  ++mn_count;
  return true;
 }
 bool pop(tpType & po_val)
 {
  std::lock_guard<Mutex> lock(mo_mutex_pop);
  if (!mp_pop_queue->empty())
  {
   //有数据可以直接取出
   po_val = std::move(mp_pop_queue->front());
   mp_pop_queue->pop();
   --mn_count;
   return true;
  }
  else
  {   
   { //尝试交换数据
    std::lock_guard<Mutex> lock(mo_mutex_push);
    if (mp_push_queue->empty())
    {
     return false; //两个队列都没数据
    }
    //有数据,可进行交换
    std::queue< tpType > * zp_tmp = mp_push_queue;
    mp_push_queue = mp_pop_queue;
    mp_pop_queue = zp_tmp;
   }
   //交换后取出数据
   po_val = std::move(mp_pop_queue->front());
   mp_pop_queue->pop();
   --mn_count;
   return true;
  }
 }
protected:
 std::atomic<uint32> mn_count;
 Queue * mp_push_queue;
 Queue * mp_pop_queue;
 Queue mc_queue_A;
 Queue mc_queue_B;
 Mutex mo_mutex_push;
 Mutex mo_mutex_pop;
};

自旋锁(SpinMutex)

自旋锁是一种用于保护多线程共享资源的锁,与一般的互斥锁(mutex)不同之处在于当自旋锁尝试获取锁的所有权时会以忙等待(busy waiting)的形式不断的循环检查锁是否可用。在多处理器环境中对持有锁时间较短的程序来说使用自旋锁代替一般的互斥锁往往能提高程序的性能。

class spin_mutex
{ 
public:
     spin_mutex() = default;
     void lock()
     {
      while (mn_flag.test_and_set(std::memory_order_acquire));
     }
     void unlock()
 {
  mn_flag.clear(std::memory_order_release);
 }
private:
 std::atomic_flag mn_flag = ATOMIC_FLAG_INIT;
};

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:7942463
帖子:1588486
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP