线程同步与队列的使用问题 - C/C++ / C++ 语言
大家好,我写了一个程序:
一个类的声明如下:
class MediaInfo
{
private:
BYTE *pd;
media_data_rec *pm;
int n;
public:
MediaInfo(int len, int vid, int aid, BYTE *buffer, int handle);
MediaInfo(const MediaInfo &m);
~MediaInfo();
media_data_rec getMdr() const;
int getHandle() const;
BYTE * getData() const;
};
然后定义了全局变量:
queue<MediaInfo> mdrQ;
CRITICAL_SECTION CriticalSection;
HANDLE ghSemaphore;
之后初始化了信号量和临界区:
ghSemaphore = CreateSemaphore( NULL, 0, 1000000, NULL);
if (ghSemaphore == NULL)
{
printf("CreateSemaphore error: %d\n", GetLastError());
return 1;
}
if (!InitializeCriticalSectionAndSpinCount(&CriticalSection,
0x00000400) )
return -1;
之后写了创建了一个线程,不断把信息放到队列mdrQ,其调用的函数是:
void CALLBACK lll(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void* pUser)
{
MediaInfo * pmi = NULL;
if (SN_DVR_STREAMDATA == dwDataType)
{
MediaInfo * pmi = new MediaInfo(dwBufSize, 0, 0, pBuffer, n);
if(!pmi)
{
writeLog("new","lll pmi");
exit(-1);
}
EnterCriticalSection(&CriticalSection);
mdrQ.push(*pmi);
LeaveCriticalSection(&CriticalSection);
//发出信号量,通知另一个线程,mdrQ有数据可以上传
if (!ReleaseSemaphore(ghSemaphore, 1, NULL) )
{
printf("ReleaseSemaphore error: %d\n", GetLastError());
}
printf("Data length:%d \n", dwBufSize);
}
之后创建了另一个一个线程不停地从队列mdrQ得到信息上传,其调用的函数是:
DWORD WINAPI UpLoad( LPVOID lpParam )
{
DWORD dwWaitResult;
BOOL bContinue=TRUE;
int n, i=0, dwBufSize,j = 24;
BYTE * pBuffer = NULL;
while(bContinue)
{
//等待信号量
dwWaitResult = WaitForSingleObject(
ghSemaphore, // handle to semaphore
INFINITE); // zero-second time-out interval
if (WAIT_OBJECT_0 == dwWaitResult)
{
while(!mdrQ.empty())
{
MediaInfo mediaInfo=mdrQ.front();
media_data_rec mdr=mediaInfo.getMdr();
n=mediaInfo.getHandle();
g_module_info.ev_callbcak(g_module_info.module_handle,n,EV_MEDIA,(char*)&mdr,mdr.datalen); //上传数据
EnterCriticalSection(&CriticalSection);
mdrQ.pop();
LeaveCriticalSection(&CriticalSection);
}
}
}
return TRUE;
}
请问为什么程序运行到一定时间之后,就会出现"Debug Assertion Failed! Expression: deque iterator not dereferencable "出错呢?
我参考了相应资料,推测可能是mdrQ的操作问题引起的。但是对于“mdrQ.push(*pmi);”和“mdrQ.pop();”已经做了判断mdrQ是否为空,并且写了临界区(CriticalSection)的保护,为什么还会出问题呢?
谢谢大家的指点!
[解决办法]
线程安全一类的问题也许没有那么容易遇到。
据我经验看,你也就是STL的用法没有保护好,没事用empty试一试。。。能不能pop啊什么的。
[解决办法]
while(!mdrQ.empty())
试试改为if(!mdrQ.empty())
{}
else
bContinue = FALSE;
[解决办法]
UpLoad函数里面的临界区操作你必须将mdrQ.front();都包括进去
[解决办法]
media_data_rec mdr=mediaInfo.getMdr();
n=mediaInfo.getHandle();
g_module_info.ev_callbcak(g_module_info.module_handle,n,EV_MEDIA,(char*)&mdr,mdr.datalen); //上传数据
mdrQ.pop();
全部作为一个临界区比较妥当。否则,按照你的做法,那么在上传数据完成后,这个时候发生线程切换。那么你后面的pop()就不是你想要的结果了。
[解决办法]
MediaInfo mediaInfo=mdrQ.front();
这里应该是有问题的
典型的情况是:
你读取queue的ptr后,另外一个线程会让这个地址失效(一般来说,queue都是采用数组来实现的,再记一个头尾标记
一旦达到某个总容量的一个比例,就会扩容)
这里有好几个解决方案:
1.把以上语句加入到lock范围内
2.采用list实现queue
3.采用lock-free 的数据结构实现
4.自己写单向链表 no-lock实现
[解决办法]
因为实现queue的底层容器可以是deque也可以是list,如果说是deque那么你那个就会有问题的。如果是list应该是没问题的。但是在我看来,全部作为一个临界区的更保险。
[解决办法]
3楼的说法是正确的。
UpLoad中,mdrQ的pop操作影响empty的结果,多条UpLoad线程都通过了empty的判断,多次pop之后,后可能令mdrQ变成空,再front或者pop就出错了。
所以,从empty到pop都要放到同一个临界区里面。
[解决办法]
"Debug Assertion Failed! Expression: deque iterator not dereferencable "
很清楚说明了,queue应该是默认使用deque容器的,而此容器的副作用就是删除某节点导致所有其后的迭代器引用失效,而你用的是pop,和front访问就是说问题发生在“当队列还有1个节点,但有几个对象需要访问,而其中某个对象把其删除之后”
好好看看同步和添加一些安全性防范措施吧。
[解决办法]
void say(void* lp)
{
cout < < "我草~! " < <endl;
}
for(int i=0;i <10000;++i)
{
createthread(..,say,..);
}
我的say线程函数也只有一个,但是你知道我说了多少句:我草~!吗?
[解决办法]
弱弱的说下:
LZ能保证线程每次
MediaInfo * pmi = new MediaInfo(dwBufSize, 0, 0, pBuffer, n);都成功吗????
会不会有这种可能:
因为你的程序运行一段时间后,new失败了,导致上传线程的mdrQ.front无法解引用
[解决办法]
你应该是按照我说的先做好同步吧。你这里提示已经告诉你了某些迭代器失效。至于原因,对于STL来说,本身实现就没有一个标准,而且各种极限情况 比如导致内存重分配等等这些情况我们是没办法一个一个去想的。如果你做好同步还有这个问题。那么就考虑从其他方面入手了。