前段时间无意中想到了无锁队列,于是实现之,实现代码如下:
结果:
template<class T0>
bool CUnlockQue<T0>::Push(T0 &tValue, int nWaitTime)
{
struct timespec TimeWait;
//已经有足够多的push操作向列队不同位置写入数据
while (0 >= AtomDec(m_nAbleWriteCount, 1))
{
AtomAdd(m_nAbleWriteCount, 1);
TimeWait.tv_sec = time(NULL) + nWaitTime;
TimeWait.tv_nsec = 0;
// 等待唤醒
pthread_mutex_lock(&m_Mutex);
if (ETIMEDOUT == pthread_cond_timedwait(&m_CondWait, &m_Mutex, &TimeWait)) // 超时
{
pthread_mutex_unlock(&m_Mutex);
return false;
}
pthread_mutex_unlock(&m_Mutex);
}
int nPushPos = AtomAdd(m_nPush, 1);
nPushPos = nPushPos % m_nMaxCap;
/*
只有在NPop并发情况下,因Pop无序完成,第一个位置的Pop未完成,后面的Pop就先完成提示有空位
因为该类只允许1对N,所以必然是单线程Push,所以条件内push控制变量不需要原子操作
进行不停的尝试,找到"Pop“完成
*/
while (!m_VecElement[nPushPos].bIsEmpty)
{
/*
m_nPush --;
m_nWriteAbleCount ++;
return false;
*/
}
m_VecElement[nPushPos].Element = tValue;
m_VecElement[nPushPos].bIsEmpty = false;
AtomAdd(m_nAbleReadCount, 1);
return true;
}
template<class T0>
bool CUnlockQue<T0>::Pop(T0 &tValue)
{
if (0 >= m_nAbleReadCount)
{
return false;//空列队
}
if ( 0 >= AtomDec(m_nAbleReadCount, 1)) //已经有足够多的pop操作读取列队不同位置的数据
{
AtomAdd(m_nAbleReadCount, 1);
return false;
}
pthread_cond_broadcast(&m_CondWait);
int nPopPos = AtomAdd(m_nPop, 1);
nPopPos = m_nPop % m_nMaxCap;
while (m_VecElement[nPopPos].bIsEmpty)
{
/*
m_nAbleReadCount ++;
m_nPop --;
*/
}
tValue = m_VecElement[nPopPos].Element;
m_VecElement[nPopPos].bIsEmpty = true;
AtomAdd(m_nAbleWriteCount, 1);
return true;
}
template<class T0>
int CUnlockQue<T0>::AtomAdd(int &nElement, int nValue)
{
return __sync_fetch_and_add(&nElement, nValue);
}
template<class T0>
int CUnlockQue<T0>::AtomDec(int &nElement, int nValue)
{
nValue *= -1;
return __sync_fetch_and_add(&nElement, nValue);
}
说明:
1: 通过__sync_fetch_and_add来实现无所队列,(还有可以用cas指令来实现无锁队列,不知道效果如何)
2: 这里本想用条件变量来实现当队列满阻塞等待,发现无法实现之。(不知道是否有好方法),于是乎采取了轮训方法进行实现等待(消耗CPU)
测试:
void* thread_input(void *lpParameter)
{
int nValue;
while (1)
{
nValue = __sync_fetch_and_add(&g_CalcNums, 1);
if (!g_Que.Push(nValue, 5))
{
LOG_APP_WARN("Value=%d,Push Failed!", nValue);
}
}
return (void *)1;
}
void *thread_OutPut(void *lpParameter)
{
int nValue;
while (1)
{
if (!g_Que.Pop(nValue))
{
LOG_APP_WARN("Empty");
continue;
}
LOG_APP_DEBUG("Value=%d", nValue);
}
return (void *)1;
}
结果:
分别开了3个线程push数据,1个线程pop读数据。测试结果:约450000/s,,对于无锁队列我感觉很奇怪,测试效率貌似很低。
于是采用加锁的push方法:Pop线程采用swap机制。测试结果:约2351230/s。
结论
无锁队列的效率怎么这么低,照理说不应该。__sync_fetch_and_add其实是个原子指令,gcc独有的。猜想类似于锁。所以无锁队列的"锁"很多,个人觉得如果还是采用“锁交换机制”更好。