头图

无锁数据结构

    • 无锁并非真正无锁,正如零拷贝并非零次拷贝。
  • 实现

    • 多生产者多消费者队列

      // 基于自旋锁(或者CAS,原理相同,后者实现较为复杂)
      // 通过atomic库中atomic_flag类进行实现自旋达到多生产者多消费者同步。
      #include <iostream>
      #include <atomic>
      
      class Queue {
          atomic_flag spin_push_flag = false, spin_pop_flag = false;
          
      public:
             void Push() {
              // 自旋+上锁
              while (spin_push_flag.test_and_set())
                  ;
              
              // working...
              
              // 解锁
              spin_push_flag.clear();
          } 
          
          void Pop() {
              // 自旋+上锁
              while (spin_pop_flag.test_and_set())
                  ;
              
              // working...
              
              // 解锁
              spin_pop_flag.clear();
          }
      };
    • 单生产者单消费者队列

      // 类似内核kfifo
      #include <iostream>
      #include <mutex>
      
      class Queue {
      private:
          mutex mutex_push, mutex_pop;
          
      public:
             void Push() {
              lock_gurad<mutex> lck(mutex_push);
              
              // working...
          } 
          
          void Pop() {
              lock_gurad<mutex> lck(mutex_pop);
              
              // working...
          }
      };
  • 测试

    • 前提:每次主线程十次循环建立100个线程访问队列,即1k线程并发。取100次平均值。一共三个版本:互斥锁单线程访问a、单生产者单消费者b、多生产者多消费者c

      场景结果
      Push和Pop接口不工作a和b时间相仿;c是前二100倍
      Push和Pop接口处理一些简单工作a和b时间相仿;c是前二的10倍
      Push和Pop接口处理一些较耗时工作b最快;c其次,为b的2倍;a最慢,为b的7-8倍
  • 总结

    • 综合来看:单生产者单消费者模型效率最优(Linux内核kfifo)。不过具体使用何种模型实现线程安全,要根据实际场景进行选择,并且多测试,才能够达到最佳模型的选择。此处仍有一点未提及:若要实现多线程并发访问数据结构,即单生产者单消费者或者多生产者多消费者,仍需要尽量避免共同数据的访问防止segment fault。

kkkK2008
1 声望0 粉丝