一重现该错误
由于最近在一个项目中总是会莫名其妙的出现"User Breakpoint called from code at"错误,该错误不是每次运行的时候都出现,而是是不是会出现(Debug下),有时候10分钟,有时候则几个小时。这到底是由什么引起的呢?
由于每次运行的时候,都是与CString类相关,而且又是在多线程编程模式下,于是,就想复制该情况,重现该错误。我是这么做的。
VC6基于对话框模式新建一个工程,在CCTestMutiThreadDlg的cpp文件中所有函数外部创建一个CString类全局变量和三个操作该变量的线程函数。
CString g_str;
UINT Proc1(LPVOID param)
{
while(1)
g_str = "FisrtThread";
return 0;
}
UINT Proc2(LPVOID param)
{
while(1)
g_str = "SecondThread";
return 0;
}
UINT Proc3(LPVOID param)
{
while(1)
g_str = "ThirdThread";
return 0;
}
AfxBeginThread(Proc1,this);
AfxBeginThread(Proc2,this);
AfxBeginThread(Proc3,this);
Sleep(100000);
二修复错误
上述错误是由于多线程访问一全局CString时,由于CString不是线程安全对象,所以导致出现错误。怎样改呢?很显然,要对线程进行同步处理。这里我们用临界区的方法进行同步。
首先在CCTestMutiThreadDlg的cpp文件中定义一个全局临界区对象CRITICAL_SECTION g_cs;
然后在按钮响应函数中线程开始之前,对临界区进行初始化和最后的删除
InitializeCriticalSection(&g_cs);
AfxBeginThread(Proc1,this);
AfxBeginThread(Proc2,this);
AfxBeginThread(Proc3,this);
InitializeCriticalSection(&g_cs);
Sleep(100000);
DeleteCriticalSection(&g_cs);
最后是同步各个线程
UINT Proc1(LPVOID param)
{
while(1)
{
EnterCriticalSection(&g_cs);
g_str = "FisrtThread";
LeaveCriticalSection(&g_cs);
}
return 0;
}
之后运行该程序,就没有问题了。
三延展
1把全局的CString对象换成全局的int对象后,不存在上述问题
2在每个线程中加入Sleep(100);函数,暂时没有出现上述问题
3在Release版本下运行,竟然没有问题!
另,网上有这样一段话:
现象
当g_test为int,short char时,不存在多线程交叉读写错误的问题
当g_test为double, float, __int64时,存在多线程交叉读写错误的问题,对于__int64,当赋值小于0xFFFFFFFF时不出错,当大于0xFFFFFFFF时出错
当g_test为CString时,存在交叉读写错误,有时候程序崩溃
另:不加Sleep(1)机器卡死过,CPU占用率达到100%,4个核心占用率全满,可以保证运行在多核环境下
结论
1.对于int,short,char,BOOL等小于等于4字节的简单数据类型,如果无逻辑上的先后关系,多线程读写可以完全不用加锁
2.尽管float为4字节,多线程访问时也需要加锁
3.对于大于4字节的简单类型,比如double,__int64等,多线程读写必须加锁。
4.对于所有复杂类型,比如类,结构体,容器等类型必须加锁
综合来看,加锁就是了。