1 现象:问题描述
多线程map无保护操作,引起map的查找操作出现问题,导致预处理程序停止处理话单。
2 关键过程:根本原因分析
int CProcBillImp::EndAFile(UINT4 nFileSN)
{
int nRet = 0;
TFileStat* pFileStat = m_pFileHouse->GetFileStat(nFileSN);
if(pFileStat != NULL)
{
pFileStat->nAnswerCount++;
//如果该文件的"结束应答"消息个数等于处理通道个数,
//则结束该文件的处理
if(pFileStat->nAnswerCount >= m_nProcChlNum)
{
char szFileName[MAX_PATH];
const TCollectInfo* pInfo = m_pFileHouse->GetCollectInfo(pFileStat->nCollectNo);
SNPRINTF(szFileName, MAX_PATH, "%s/%s/%s", pInfo->sName.c_str(),
pFileStat->szMidPath, pFileStat->szFileName);
//结束每一个类型通道中该文件对应的错单文件
// … …
TFileStat* CFileHouseImp::GetFileStat(UINT4 nFileSN)
{
FILE_STAT::iterator it = m_FileStatList.find(nFileSN);
if(it != m_FileStatList.end())
{
return it->second;
}
return NULL;
}
在FileHouse中保存一个当前正在处理的话单文件列表。
这个列表由两个线程进行操作:
话单处理线程将处理过的话单加入这个列表;
话单结束线程从这个列表中删除已经成功结束处理的话单文件。
由于在取得列表时没有加锁,在多线程竞争的情况下,会在查找文件ID的时候,其他线程在改变map元素的个数,导致找不到与文件ID对应的文件状态对象。也就是这个文件状态对象会一直保留在文件列表中。当列表的长度大于3后,话单处理线程会停止处理文件
3 结论:解决方案及效果
在文件状态列表中查找对象前加锁控制线程并发,保证在查找的时候,不会有map的插入操作(只为一个线程所有,删除和查找为一个线程)
TFileStat* CFileHouseImp::GetFileStat(UINT4 nFileSN)
{
m_MutexForStat.acquire();
FILE_STAT::iterator it = m_FileStatList.find(nFileSN);
if(it != m_FileStatList.end())
{
TFileStat* filsStat = it->second;
m_MutexForStat.release();
return filsStat;
}
m_MutexForStat.release();
char szTmpBuf[g_nTmpBuffLen];
SNPRINTF(szTmpBuf, g_nTmpBuffLen, "%s%d", CONVERT_MOD_NAME, m_nModNo);
szTmpBuf[g_nTmpBuffLen - 1] = '\0';
TRACE(szTmpBuf, "Can not find the stat of file(%8d)",nFileSN);
return NULL;
}
4 经验总结:预防措施和规范建议
当在多线程环境下,某些提供读写方法的容器对象的操作需要注意对其进行操作保护,保证数据正确性。
5 备注
6 考核点
多线程,容器,同步保护。
7 试题
std::map MapOpName;
MapOpName opName;
std:string ThreadA::GetOpName
{
//查找OperatorName
MapOpName::iterator it = opName.find(m_nIMSI);
if (it != opName.end())
{
return it.second();
}
}
TheadB::ClearData()
{
MapOpName::iterator it = opName.begin();
while(it != opName.end())
{
delete it->second;
++it;
}
}
对这段代码下面说法中正确的是:BC
A、代码正确,没有问题;
B、代码在多线程环境下会发生不可知的错误;
C、在单线程下代码能够正确工作;
D、以上说法都不对。
阅读(622) | 评论(0) | 转发(0) |