并发暗送秋波无锁的无尽的队列

it2026-08-21  4

并发暗送秋波无锁的无尽的队列

 

Author:Echo Chen(斌)

Email:chenb19870707@gmail.com

Blog:Blog.csdn.net/chen19870707

Date:October 10th, 2014

    

       前面在《眉目传情之匠心独运的kfifo》一文中具体解析了 linux  内核并发无锁环形队列kfifo的原理和实现,kfifo鬼斧神工,博大精深。让人叹为观止。但遗憾的是kfifo为内核提供服务。并未开放出来。

剑不试则利钝暗,弓不试则劲挠诬,鹰不试则巧拙惑,马不试则良驽疑,光说不练是不能学到精髓的,以下就动手实现自己的并发无锁队列UnlockQueue(单生产者单消费者)。

 

一、UnlockQueue声明

 

1: #ifndef _UNLOCK_QUEUE_H 2: #define _UNLOCK_QUEUE_H 3:  4: class UnlockQueue 5: { 6: public: 7:     UnlockQueue(int nSize); 8:     virtual ~UnlockQueue(); 9:  10:     bool Initialize(); 11:  12:     unsigned int Put(const unsigned char *pBuffer, unsigned int nLen); 13:     unsigned int Get(unsigned char *pBuffer, unsigned int nLen); 14:  15:     inline void Clean() { m_nIn = m_nOut = 0; } 16:     inline unsigned int GetDataLen() const { return  m_nIn - m_nOut; } 17:  18: private: 19:     inline bool is_power_of_2(unsigned long n) { return (n != 0 && ((n & (n - 1)) == 0)); }; 20:     inline unsigned long roundup_power_of_two(unsigned long val); 21:  22: private: 23:     unsigned char *m_pBuffer;    /* the buffer holding the data */ 24:     unsigned int   m_nSize;        /* the size of the allocated buffer */ 25:     unsigned int   m_nIn;        /* data is added at offset (in % size) */ 26:     unsigned int   m_nOut;        /* data is extracted from off. (out % size) */ 27: }; 28:  29: #endif

 

UnlockQueue与kfifo 结构同样同样,也是由一下变量组成:

UnlockQueuekfifo作用m_pBufferbuffer用于存放数据的缓存m_nSizesize缓冲区空间的大小。圆整为2的次幂m_nInin指向buffer中队头m_nOutout指向buffer中的队尾UnlockQueue的设计是用在单生产者单消费者情况下,所以不须要锁lock假设使用不能保证不论什么时间最多仅仅有一个读线程和写线程,必须使用该lock实施同步。

 

二、UnlockQueue构造函数和初始化

 

1: UnlockQueue::UnlockQueue(int nSize) 2: :m_pBuffer(NULL) 3: ,m_nSize(nSize) 4: ,m_nIn(0) 5: ,m_nOut(0) 6: { 7:     //round up to the next power of 2 8:     if (!is_power_of_2(nSize)) 9:     { 10:         m_nSize = roundup_power_of_two(nSize); 11:     } 12: } 13:  14: UnlockQueue::~UnlockQueue() 15: { 16:     if(NULL != m_pBuffer) 17:     { 18:         delete[] m_pBuffer; 19:         m_pBuffer = NULL; 20:     } 21: } 22:  23: bool UnlockQueue::Initialize() 24: { 25:     m_pBuffer = new unsigned char[m_nSize]; 26:     if (!m_pBuffer) 27:     { 28:         return false; 29:     } 30:  31:     m_nIn = m_nOut = 0; 32:  33:     return true; 34: } 35:  36: unsigned long UnlockQueue::roundup_power_of_two(unsigned long val) 37: { 38:     if((val & (val-1)) == 0) 39:         return val; 40:  41:     unsigned long maxulong = (unsigned long)((unsigned long)~0); 42:     unsigned long andv = ~(maxulong&(maxulong>>1)); 43:     while((andv & val) == 0) 44:         andv = andv>>1; 45:  46:     return andv<<1; 47: }

 

1.在构造函数中,对传入的size进行2的次幂圆整。圆整的优点是能够将m_nIn % m_nSize 能够转化为 m_nIn  & (m_nSize – 1),取模运算”的效率并没有 “位运算” 的效率高。

2.在构造函数中,未给buffer分配内存,而在Initialize中分配。这样做的原因是:我们知道在new UnlockQueue的时候有两步操作。第一步分配内存,第二步调用构造函数,假设将buffer的分配放在构造函数中,那么就可能 buffer 就可能分配失败,而后面用到buffer,还须要判空。

三、UnlockQueue入队和出队操作

 

1: unsigned int UnlockQueue::Put(const unsigned char *buffer, unsigned int len) 2: { 3:     unsigned int l; 4:  5:     len = std::min(len, m_nSize - m_nIn + m_nOut); 6:  7:     /* 8:      * Ensure that we sample the m_nOut index -before- we 9:      * start putting bytes into the UnlockQueue. 10:      */ 11:     __sync_synchronize(); 12:  13:     /* first put the data starting from fifo->in to buffer end */ 14:     l = std::min(len, m_nSize - (m_nIn  & (m_nSize - 1))); 15:     memcpy(m_pBuffer + (m_nIn & (m_nSize - 1)), buffer, l); 16:  17:     /* then put the rest (if any) at the beginning of the buffer */ 18:     memcpy(m_pBuffer, buffer + l, len - l); 19:  20:     /* 21:      * Ensure that we add the bytes to the kfifo -before- 22:      * we update the fifo->in index. 23:      */ 24:     __sync_synchronize(); 25:  26:     m_nIn += len; 27:  28:     return len; 29: } 30:  31: unsigned int UnlockQueue::Get(unsigned char *buffer, unsigned int len) 32: { 33:     unsigned int l; 34:  35:     len = std::min(len, m_nIn - m_nOut); 36:  37:     /* 38:      * Ensure that we sample the fifo->in index -before- we 39:      * start removing bytes from the kfifo. 40:      */ 41:     __sync_synchronize(); 42:  43:     /* first get the data from fifo->out until the end of the buffer */ 44:     l = std::min(len, m_nSize - (m_nOut & (m_nSize - 1))); 45:     memcpy(buffer, m_pBuffer + (m_nOut & (m_nSize - 1)), l); 46:  47:     /* then get the rest (if any) from the beginning of the buffer */ 48:     memcpy(buffer + l, m_pBuffer, len - l); 49:  50:     /* 51:      * Ensure that we remove the bytes from the kfifo -before- 52:      * we update the fifo->out index. 53:      */ 54:     __sync_synchronize(); 55:  56:     m_nOut += len; 57:  58:     return len; 59: }

  

    入队和出队操作与kfifo同样,用到的技巧也全然同样。有不理解的童鞋能够參考前面一篇文章《眉目传情之匠心独运的kfifo》。这里须要指出的是__sync_synchronize()函数。因为linux并未开房出内存屏障函数,而在gcc4.2以上版本号提供This builtin issues a full memory barrier,有兴趣同学能够參考Built-in functions for atomic memory access。

 

四、測试程序

 

如图所看到的,我们设计了两个线程,一个生产者随机生成学生信息放入队列,一个消费者从队列中取出学生信息并打印,能够看到整个代码是无锁的

1: #include "UnlockQueue.h" 2: #include <iostream> 3: #include <algorithm> 4: #include <pthread.h> 5: #include <time.h> 6: #include <stdio.h> 7: #include <errno.h> 8: #include <string.h> 9:  10: struct student_info 11: { 12:    long stu_id; 13:    unsigned int age; 14:    unsigned int score; 15: }; 16:  17: void print_student_info(const student_info *stu_info) 18: { 19:     if(NULL == stu_info) 20:         return; 21:  22:     printf("id:%ld\t",stu_info->stu_id); 23:     printf("age:%u\t",stu_info->age); 24:     printf("score:%u\n",stu_info->score); 25: } 26:  27: student_info * get_student_info(time_t timer) 28: { 29:      student_info *stu_info = (student_info *)malloc(sizeof(student_info)); 30:      if (!stu_info) 31:      { 32:         fprintf(stderr, "Failed to malloc memory.\n"); 33:         return NULL; 34:      } 35:      srand(timer); 36:      stu_info->stu_id = 10000 + rand() % 9999; 37:      stu_info->age = rand() % 30; 38:      stu_info->score = rand() % 101; 39:      //print_student_info(stu_info); 40:      return stu_info; 41: } 42:  43: void * consumer_proc(void *arg) 44: { 45:      UnlockQueue* queue = (UnlockQueue *)arg; 46:      student_info stu_info; 47:      while(1) 48:      { 49:          sleep(1); 50:          unsigned int len = queue->Get((unsigned char *)&stu_info, sizeof(student_info)); 51:          if(len > 0) 52:          { 53:              printf("------------------------------------------\n"); 54:              printf("UnlockQueue length: %u\n", queue->GetDataLen()); 55:              printf("Get a student\n"); 56:              print_student_info(&stu_info); 57:              printf("------------------------------------------\n"); 58:          } 59:      } 60:      return (void *)queue; 61: } 62:  63: void * producer_proc(void *arg) 64:  { 65:       time_t cur_time; 66:       UnlockQueue *queue = (UnlockQueue*)arg; 67:       while(1) 68:       { 69:           time(&cur_time); 70:           srand(cur_time); 71:           int seed = rand() % 11111; 72:           printf("******************************************\n"); 73:           student_info *stu_info = get_student_info(cur_time + seed); 74:           printf("put a student info to queue.\n"); 75:           queue->Put( (unsigned char *)stu_info, sizeof(student_info)); 76:           free(stu_info); 77:           printf("UnlockQueue length: %u\n", queue->GetDataLen()); 78:           printf("******************************************\n"); 79:           sleep(1); 80:       } 81:      return (void *)queue; 82:   } 83:  84:  85: int main() 86: { 87:     UnlockQueue unlockQueue(1024); 88:     if(!unlockQueue.Initialize()) 89:     { 90:         return -1; 91:     } 92:  93:     pthread_t consumer_tid, producer_tid; 94:  95:     printf("multi thread test.......\n"); 96:  97:     if(0 != pthread_create(&producer_tid, NULL, producer_proc, (void*)&unlockQueue)) 98:     { 99:          fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n", 100:                  errno, strerror(errno)); 101:          return -1; 102:     } 103:  104:     if(0 != pthread_create(&consumer_tid, NULL, consumer_proc, (void*)&unlockQueue)) 105:     { 106:            fprintf(stderr, "Failed to create consumer thread.errno:%u, reason:%s\n", 107:                    errno, strerror(errno)); 108:            return -1; 109:     } 110:  111:     pthread_join(producer_tid, NULL); 112:     pthread_join(consumer_tid, NULL); 113:  114:     return 0; 115:  }

 

执行结果:

-

Echo Chen:Blog.csdn.net/chen19870707

-

版权声明:本文博主原创文章,博客,未经同意不得转载。

转载于:https://www.cnblogs.com/bhlsheji/p/4911470.html

相关资源:2020关于中国老年产业发展调查报告.doc
最新回复(0)