线程传值风险

来源:互联网 发布:数据网络加速器 编辑:程序博客网 时间:2024/05/20 06:24

线程传值风险

对于一个线程,通常可以给它传入一个LPVOID类型的参数。大致看来,这种行为与给一个函数传参没有多少差别。可能正是这种表象,使我们疏于防范,从而造成一些不易发觉的风险。下面用一些例子来说明。

...struct thread_data{    int a;    int b;};void thread_func(LPVOID lpVoid);void test_func(){    thread_data td;    memset(&td, 0, sizeof thread_data);    td.a = 1;    td.b = 2;    _beginthread(thread_func, 0, (LPVOID)&td);}int _tmain(int argc, _TCHAR* argv[]){    test_func();    getchar();    return 0;}void thread_func(LPVOID lpVoid){    thread_data* pTd = (thread_data*)lpVoid;    cout << "thread_data::a = " << pTd->a << endl;    cout << "thread_data::b = " << pTd->b << endl;    return;}

输出结果:

thread_data::a = 262661272
thread_data::b = 2095484

可以看出这个值是随机的。原因显而易见,将一个局部变量的地址,传递给了线程。在test_func函数返回后,如果再访问这个地址,将会出现“undefined behavior”,但是不会报错。因为这个局部变量的地址存在于栈内存中,栈内存是随线程一起分配的,主线程的栈内存整个程序结束前一直都存在而且尺寸也不会变。所以,保存局部变量的内存会一直存在,只是内容会被其他变量修改。因此 ,当一个局部变量被销毁后,再次通过它的地址去访问它的内容,可能会正确(见下面的例子),也可能会错误,是一个不确定的行为。这样的操作是不允许的!

//将上面的test_func()函数调用,改成下面形式int _tmain(int argc, _TCHAR* argv[]){    {        thread_data td;        memset(&td, 0, sizeof thread_data);        td.a = 1;        td.b = 2;        _beginthread(thread_func, 0, (LPVOID)&td);    }    getchar();    return 0;}

输出结果:

thread_data::a = 1
thread_data::b = 2

结果正确!

总结:除非能保证局部变量一直存在于线程生命周期内,否则不能将一个局部变量的地址传递给线程,这样会造成不确定的行为。一般,我们会采用全局变量或者堆内存传递给线程,这样可以保证传递内容的可靠性。

在给线程传递参数的过程中,还存在一个不容忽视的问题,那就是参数的同步性。假如我们将一个全局变量传入一个线程后,然后,又无意识地在其它地方修改了它,这就会使程序运行结果不在预期内。见下面的例子:

struct thread_data{    int a;    int b;};void thread_func(LPVOID lpVoid);void test_func(thread_data * ptd){    ptd->b = 3;}int _tmain(int argc, _TCHAR* argv[]){    thread_data * ptd = new thread_data;    memset(ptd, 0, sizeof thread_data);    ptd->a = 1;    ptd->b = 2;    _beginthread(thread_func, 0, (LPVOID)ptd);    test_func(ptd);    getchar();    return 0;}void thread_func(LPVOID lpVoid){    thread_data* pTd = (thread_data*)lpVoid;    Sleep(100);    cout << "thread_data::A::_x = " << pTd->a << endl;    cout << "thread_data::b = " << pTd->b << endl;    return;}

输出结果:

thread_data::a = 1
thread_data::b = 3

可能我们预期的结果为1,2!

如果不涉及到非要使用指针对象,我们可以在线程函数中,做一些小的改变去避免这种现象的发生,实现简单的同步操作。见下面例子:

int _tmain(int argc, _TCHAR* argv[]){    thread_data * ptd = new thread_data;    memset(ptd, 0, sizeof thread_data);    ptd->a = 1;    ptd->b = 2;    _beginthread(thread_func, 0, (LPVOID)ptd);    Sleep(10);  //增加休息时间,让线程先运行    test_func(ptd);    getchar();    return 0;}void thread_func(LPVOID lpVoid){    //不使用指针赋值,采用内存拷贝,这样后面涉及形参的运算就不会受到影响了    //thread_data* pTd = (thread_data*)lpVoid;    thread_data td = *(thread_data*)lpVoid;    Sleep(100);       cout << "thread_data::A::_x = " << td.a << endl;    cout << "thread_data::b = " << td.b << endl;    return;}

输出结果:

thread_data::a = 1
thread_data::b = 2

原创粉丝点击