C/C++ 多线程参数需要尽量采用动态分配的方式来分配
文章目录
在 C/C++ 多线程编程下,如果不注意,采用普通变量传递参数值给线程会有一些误区,需要特别小心。
下面浏忙绪绪就举两个例子来说明一下。
char* 参数在多线程下出现的怪异现象
最近在用 Boost 库写多线程程序时,需要启动若干个线程,这些线程分别处理不同的事情,线程会获取一个字符串参数,用来标识内容。在编写代码的时候,出现了一个很怪异的现象,例子代码如下:
<pre>
const int tNum = 4;//并发线程数 vector<boost::thread*> tBox;
for (int i=0; i < tNum; i++ ) { char strThread[20]; sprintf(strThread, “thread%d”,i); //strThread 字符串是传入的 const char* 类型 boost::thread* thread_0 = new boost::thread( StreamProcesser, param1, param2, strThread);
tBox.push_back(thread_0);
<span>boost</span>::<span>xtime</span> <span>xt</span>;
<span>boost</span>::xtime_get(&xt, <span>boost</span>::TIME_UTC);
xt.sec += 2;
<span>boost</span>::<span>thread</span>::sleep(xt);
}
for (int i=0; i < tNum; i++ ) { boost::thread* thread_0 = tBox[i]; thread_0->join(); delete thread_0; }
<p>
在线程函数启动参数中,有一个参数是 const char* 类型。如果我在线程中,没有先对 char 字符串拷贝一个副本,则当4个线程都跑起来后,再去读取这个参数,很有可能会读到同一个字符串。
</p>
<p>
这是因为编译器把 strThread 的地址编码为同一个内存地址造成的,所以,所有的线程读取的都是最后一次设置 strThread 的值。
</p>
<p>
结论:
</p>
<ol>
<li>
传入 char* 后,一定要对 char 字符串拷贝一个副本,否则这个指针指向的内容很有可能被改变;
</li>
<li>
多用 C++ 的 string,少用 char 或是 char*,用值拷贝的方式比单纯传递一个指针要更安全;
</li>
<li>
最安全的做法,应该是动态分配一个空间,用来保存传递给线程的参数值,在线程结束后再销毁该值。
</li>
</ol>
int 参数在多线程下被重复赋值的怪异现象
同样,如果传入参数是整型或是其它类型的时候,也会有上述类似的问题。拿 Win32 的 CreateThread 函数来说,同样需要保证传入的参数不被修改,例如下面的代码就非常危险:
<pre>
DWORD WINAPI CloseThreadFun( LPVOID param) { int* pHandle = (int*)param; const int handle = *pHandle;
<span>//</span><span>打印句柄
printf(“get handle is %d”, handle);
<span>return</span> 0;
}
int _tmain(int argc, _TCHAR* argv[]) { const int THREAD_NUM = 4;
<span>HANDLE</span>* <span>lphandle</span> = <span>new</span> <span>HANDLE</span>[THREAD_NUM];
<span>for</span> (<span>int</span> <span>j</span> = 0; j < THREAD_NUM; j++)
{
<span>HANDLE</span> <span>hthread</span>;
hthread = CreateThread(<span>NULL</span>, 0, CloseThreadFun, (<span>LPVOID</span>)&j, 0, <span>NULL</span>);
lphandle[j] = hthread;
}
WaitForMultipleObjects(THREAD_NUM, lphandle, TRUE, INFINITE);
<span>delete</span> [] lphandle;
<span>return</span> 0;
}
<p>
在调用 CloseThreadFun 来启动一个线程后,j 的值很有可能已经被修改掉了:线程启动总是需要时间的,而参数指针指向地址的内容,很有可能在此期间被修改了。比如,上面的代码,运行后,打印的内容如下:
</p>
<pre>
get handle is 3 get handle is 4 get handle is 4 get handle is 4
<p>
这就说明了 j 值被重复修改后,会导致线程参数不对的现象。
</p>
<p>
解决办法:用一个 int 数组把需要传入到各个线程的参数缓存起来,尽量保证地址不一样。
</p></p>
结论
传入线程的参数,应该尽量采用动态分配内存的方式来生成。否则如果采用临时变量,则随着变量生命周期的消逝,该变量的指针,很有可能会变成一个毫无意义的指针(或是被新的值覆盖,或是被成为一个遗留数)。
<p>
采用动态分配的变量作为线程启动时的参数,在线程结束后再销毁这个动态分配的变量,则是一个安全法则。
</p></p>
文章作者 cookwhy
上次更新 2012-02-02