多线程典型问题实现总结

      多线程编程中,我们会经常碰到一个线程push信息,另外一个线程按照先进先出原则处理的情况.比如多线程资源加载,网络通信等.本文总结了此种情况下的各种解决方案,并给出一个无须加锁的解.

.常用做法

   一般来说,会采用队列+lock的方式来实现,大概代码如下:

void  sendMsg(const T& tData)

{

ScopeMutex<LockType> sm(&mLock);

mVec.push(tData);

}

size_t  processMsg()

{

if(mVec.empty()) return 0;

mLock.lock();

T tData = mVec.front();

mVec.pop();

mLock.unlock();

mPf(tData);

return 1;

   }

Window,一般会采用临界区来实现加锁.尤其是XP,这样细粒度的加锁/解锁会导致频繁的进入内核模式,对性能造成很大的影响.相对来说,window7好很多.

.更好的锁

当锁粒度比较小时,采用cmpxchg指令实现的自旋锁会有更好的表现.大概逻辑如下:

  Void  lock()

{

  If(InterlockedCompareExchange(&lockCount, 1, 0)) 

  {

冲突空转并检测是否可以得到锁;

  }

}

Void unlock()

{

lockCount = 0;

}

在了解原理后,我们也可以采用成熟库的实现.比如tbb::spin_mutex

三.减少锁的频率

常用做法里面,每次获得数据都需要加/解锁.为了减少锁的频率,我们可以采用双缓冲,一个负责接受请求,另外一个处理.改进后代码逻辑大概如下.

   void  sendMsg(const T& tData)

{

ScopeMutex<LockType> sm(&mLock);

mInVec->push_back(tData);

}

size_t  processMsg()

{

if(mInVec->empty()) return 0;

mOutVec->erase(mOutVec->begin(), mOutVec->end());

std::swap(mInVec, mOutVec);

mLock.lock();

DataDeque& prossVec = *mOutVec;

mLock.unlock();

DataDeque::iterator ite = prossVec.begin(), iteEnd = prossVec.end();

for(; ite != iteEnd; ++ite)

mPf(*ite);

return prossVec.size();

}


.采用并行库的队列实现

随着多核的普及,软件的并行化也越来越具有实际意义.各个厂商也在普及软件并行化方面做了很多工作.比如openMP的支持,inteltbb.VC2010内置的并行库.这些库本身可以方便我们的应用实现,效率方面也是做了很多。本文讨论的问题,也可以采用库里面的队列实现.比如tbb::concurrent_queue.


.无锁实现

无锁的解决本文所提的问题,数据结构的选取很重要.必须要求pushpop操作上下文依赖尽量的少.

对于Intel486以上兼容处理器,读写4字节对齐的32位整数都是原子操作(64位类似,不过要求Pentium以上).

据此 ,单向链表能满足我们的要求.

在多线程环境下,要对单向链表操作进行适当的改进才能达到不加锁的效果.push线程,读写链表头,另外的处理线程只读实际处理数据时,最后一个数据不处理.

相对于其它解决方案,无锁每次都需要额外的分配结点/处理完再释放.高并发状态下,这部分也是一个很显著的开销.我们可以维护一个跟消息列表相反的链表.让内存分配/释放都在一个线程.这样就可以用一个内存链来管理他们了.把这部分额外多出来的消耗降到最低.

如果push信息内含有需要动态分配/释放的内存,采用如上方式管理信息后,分配/释放也可以在同一线程,为优化外部内存提供了可能.

代码的核心逻辑大概如下:

          inline void  sendMsg(const T& tData)

{

listMy* newList = new listMy(tData);

newList->next = *m_head;

*m_head = newList;

}

inline size_t  processMsg()

{

listMy* head = *m_head;  // 仅仅读一次.

if(head->next) //链表是否有数据

{

listMy* recordh = head;

for(;head->next;)

{

// 链表中的最后一个总是不处理.

mPf(head->idata); // todo: del

head = head->next;

}

recordh->next = NULL;

return m_buffVec.size();

}

return 0;

   }

最后附上代码链接,window/linux下均测试通过.

http://code.google.com/p/kevincodelib/downloads/list

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值