线程传值风险
来源:互联网 发布:数据网络加速器 编辑:程序博客网 时间: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
- 线程传值风险
- 线程风险
- 线程带来风险分析
- java并发1.3-线程的风险
- 深入理解线程带来的风险
- 风险管理总结(二):一种计算风险值的方法
- 多线程提高系列(1)--线程的优势以及风险性
- 【1月24日】并发(一):线程与线程风险
- Java编程中线程池的最大性能开发与风险规避
- Java编程中线程池的最大性能开发与风险规避
- Java_编程中线程池的最大性能开发与风险规避
- 历史版本兼容问题 - 使用枚举值带来的潜在风险
- 钱包风险
- 风险管理
- 风险意识
- 风险管理
- 警惕风险
- 风险管理
- 安装mysql5.7.20
- word/excel转图片
- 阿里云ubutu服务器安装jdk、tomact
- 小蓝退出舞台,谁能挺过O2O的第一个寒冬?
- 调用caffe的Python和C++的方法
- 线程传值风险
- Qt的Json数据生成--备忘
- JDBC基础----用户登录案例
- 文章标题
- React Native 聊天室置底输入框的设计
- Android实战系列(二)---多用户类型登录(webview)
- Swing 有关JList的问题
- java调用dll文件
- 建设部,住建部,住房和城乡建设部,区别?