锁是高性能程序的杀手,但是为了保证数据的一致性,在多线程的应用环境下又不得不加锁。但是在某些特殊的场景下, 是可以通过优化数据结构来达到无锁的目的。那么我们就来看一下如何实现一个无锁队列。
队列:众所周知,就是先进先出。 出队列的时候从队列头取出一个结点;入队列的时候,将结点添加到队列尾部。当多线程同时操作一个队列读写时,显然就需要加锁。但是在单读单写的这种多线程应用时,是可以做到无锁的。直接上代码
#ifndef _QUEUE_H_ #define _QUEUE_H_ template<class T> class node { public: T* pdata; node* next; }; template<class T> class queue { private: node<T> *front; node<T> *tear; public: queue(); ~queue(); bool isempty(); T* pop(); void push(T *p); }; template<class T> queue<T>::queue() { node<T> *pnode = new node<T>(); front = pnode; tear = pnode; } template<class T> queue<T>::~queue() { node<T> *cur = front; node<T> *next = NULL; while(cur) { next = cur->next; delete cur->pdata; delete cur; cur = next; } } template<class T> bool queue<T>::isempty() { if(front == tear) return true; return false; } template<class T> void queue<T>::push(T *p) { node<T> *pnode = new node<T>(); tear->pdata = p; tear->next = pnode; tear = pnode; } template<class T> T* queue<T>::pop() { if(isempty()) return NULL; node<T>* pnode = front; T* p = pnode->pdata; front = front->next; delete pnode; return p; } #endif
下面是测试代码:
#include <stdio.h> #include <pthread.h> #include "queue.h" void* fun1(void *p) { queue<int> *q = (queue<int>*)p; int i = 0; while(1) { int *tmp = q->pop(); if(tmp) { if(*tmp != i++) printf("err\n"); delete tmp; } } } void* fun2(void *p) { queue<int> *q = (queue<int>*)p; int i = 0; while(1) { int *tmp = new int(i++); q->push(tmp); } } int main() { queue<int> q; pthread_t t1,t2; pthread_create(&t1,NULL ,fun1 ,&q); pthread_create(&t2,NULL, fun2 ,&q); pthread_join(t1,NULL); pthread_join(t2,NULL); return 0; }
原理其实很简单,就是在队列的末尾添加一个空节点。这样在队列非空的情况下,front结点与tear结点中间至少间隔一个结点。那么当两个线程同时插入与取出结点时,就不会存在同时操作front与tear的情况,从而保证不会出现race condition
时间: 2024-10-13 13:07:12