记一次协程中使用线程锁造成的问题以及各种锁的总结
锁
我习惯把进程或线程同步互斥的控制方法称之为”锁”,自旋锁、互斥锁、信号量、临界区各种各样的锁一大堆,这些名字看起来都特别高大上,但是其实他们就是实现了线程间对共享数据的一个安全访问,有些锁是可以用在不同进程的线程间的,比如互斥锁和信号量。有些锁可以设置进入这个锁的次数,比如信号量。如果你的程序不需要在多个进程的线程之间加锁,那么使用临界区就可以了。
我们的项目使用的大部分是临界区,在codis和协程上线后出现了一个问题,由于每个玩家是一个单独的协程,在多线程处理一块共享数据的时候,不同的协程进入同一个函数进行加锁操作,因为函数的后续进行了redis的异步访问,这些协程可能在没有解锁的情况下被挂起,如果这个函数的访问次数增多,那么其他需要处理共享数据的线程就会等待,造成某些逻辑的卡死。由于第一时间就想到了该问题的解决办法,所以并没有继续深入研究导致程序假死的具体步骤。
记录一下出问题的函数以及解决后的函数:
///获得角色的摘要信息指针
TRoleData * CRoleDataService::FindRoleData(const std::string & _roleid)
{
SVR_BASE::CSVRCriticalEnter t_autolock(&m_lock);
RoleMap::const_iterator t_it = m_mapRoleByRoleId.find(_roleid);
if(t_it == m_mapRoleByRoleId.end())
{
SVR_BASE::coroutine_t t_coroutine = SVR_BASE::co_current();
MASSERT( t_coroutine!=NULL );
TRoleData * t_ptrRole = new TRoleData();
t_ptrRole->strRoleId = "";
if ( true == CRoleDataService::Instance._redis_command_cb( REDIS_CLT::HGETALL(ROLE_SUMMARY::_SUMMARY_ROLE_+_roleid),
std_tr1::bind(_co_rolesummary_callback, t_coroutine, t_ptrRole, std_tr1::_1 )))
{
SVR_BASE::co_wait_io_complete();
}
if("" != t_ptrRole->strRoleId)
{
m_mapRoleByRoleId.insert(std::make_pair(_roleid,t_ptrRole));
}
else
{
delete t_ptrRole;
t_ptrRole = NULL;
}
return t_ptrRole;
}
else
{
return t_it->second;
}
}
上面的函数使用了我们项目中自动释放的临界区对象SVR_BASE::CSVRCriticalEnter来对m_mapRoleByRoleId数据进行加锁,这个对象的生命周期到函数退出为止,但是 SVR_BASE::co_wait_io_complete() 会把该协程挂起,如果这个协程不再被激活,那么锁将永远不会被解开,其他线程中用到这个锁的地方就会一直处于等待状态。修改后的代码:
///获得角色的摘要信息指针
TRoleData * CRoleDataService::FindRoleData(const std::string & _roleid)
{
m_lock.lock();
RoleMap::const_iterator t_it = m_mapRoleByRoleId.find(_roleid);
if(t_it != m_mapRoleByRoleId.end())
{
m_lock.unlock();
return t_it->second;
}
m_lock.unlock();
SVR_BASE::coroutine_t t_coroutine = SVR_BASE::co_current();
MASSERT( t_coroutine!=NULL );
TRoleData * t_ptrRole = new TRoleData();
t_ptrRole->strRoleId = "";
if ( true == CRoleDataService::Instance._redis_command_cb( REDIS_CLT::HGETALL(ROLE_SUMMARY::_SUMMARY_ROLE_+_roleid),
std_tr1::bind(_co_rolesummary_callback, t_coroutine, t_ptrRole, std_tr1::_1 )))
{
SVR_BASE::co_wait_io_complete();
}
if("" != t_ptrRole->strRoleId)
{
m_lock.lock();
t_it = m_mapRoleByRoleId.find(_roleid);
if ( t_it!= m_mapRoleByRoleId.end() )
{
delete t_it->second;
m_mapRoleByRoleId.erase(t_it);
}
m_mapRoleByRoleId.insert(std::make_pair(_roleid,t_ptrRole));
m_lock.unlock();
}
else
{
delete t_ptrRole;
t_ptrRole = NULL;
}
return t_ptrRole;
}
修改后的代码只在操作m_mapRoleByRoleId这块共享数据的时候进行了加解锁操作,保证了锁不会被协程挂起。