有一个供多个线程调用的"全部函数",请问该函数是线程安全的吗?

czb 2007-12-14 01:31:28
比如有下面的一个"全局函数":
void parseData(MyStruct* pMyStruct)
{
.........
}
下面是两个线程函数,其内部都调用了parseData(MyStruct* MyStruct)方法:
UINT workAThread(LPVOID CompletionPortID)
{
....................................
MyStruct* pMyStruct = new MyStruct();
parseData(pMyStruct);
....................................
}
UINT workBThread(LPVOID CompletionPortID)
{
....................................
MyStruct* pMyStruct = new MyStruct();
parseData(pMyStruct);
....................................
}

那么"全局函数"void parseData(MyStruct* pMyStruct)到底是不是线程安全的呢?

由于是多线程,我们可知workAThread和workAThread都有自己的线程运行空间,那么void parseData(MyStruct* pMyStruct)到底会不会复制两份代码到workAThread和workAThread各自的运行空间中,还是真的作为一个全局函数?如果是一个真的全局函数,我们在编写函数void parseData(MyStruct* pMyStruct)时该注意些什么呢?

期待各位高手的回答,并说明理由
...全文
180 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
hejun779 2007-12-14
  • 打赏
  • 举报
回复
难道寄存器在各个线程里也是各有各的互不影响?

寄存器在切换线程的时候,会把当前的执行状态进行备分,CPU是按线程进行时间片的划分,所以cpu会为每个线程保存一个该线程的当前状态,当然这些数据不会放到内存中,因为这样会在频繁的切换线程中耗费大量的资源,所以一般cpu会配备内部的储存器,也就是所说的高速缓存,现在一般的cpu都有一级缓存和二级缓存..
intel在p4中的一级缓存只有4k,并且这4k的空间又固定了指令缓冲区和数据缓冲区,而amd就采用了大的一级缓存,例如:Athron xp的一级缓存高达256k,并且这256k的空间供指令和数据共享,所以尽管当时的p4主频高,可性能却跟一个低主频的Athron差不多的原因...

czb 2007-12-14
  • 打赏
  • 举报
回复
我是楼主,非常感谢大家的参与

这里的两个线程不考虑全部变量与静态变量的调用,即就只有下面两个线程函数所写的两个调用
UINT workAThread(LPVOID CompletionPortID)
{
....................................
MyStruct* pMyStruct = new MyStruct();
parseData(pMyStruct);
....................................
}
UINT workBThread(LPVOID CompletionPortID)
{
....................................
MyStruct* pMyStruct = new MyStruct();
parseData(pMyStruct);
....................................
}
线程A和线程B都是自己new了一个MyStruct,然后各自调用函数parseData.

前面的robotom和wang_junjie说的有一定道理,即每个线程都有单独的盏,parseData函数的代码为线程共享,有点明白了.

不过各线程共享parseData函数的代码,那么workAThread在调用parseData(pMyStruct)时和workBThread在调用parseData(pMyStruct)时,操作系统对这两个线程的函数调用要不要自动提供互斥,以防止他们的栈数据和寄存器数据发生混乱?即如果workAThread刚好正在执行parseData(pMyStruct)的函数体时,此时若workBThread也对parseData(pMyStruct)提出调用要求,那么workBThread需不需要等到workAThread的调用结束后才能继续调用parseData(pMyStruct)函数?

1、如果操作系统需要提供互斥,假设在parseData(pMyStruct)函数体里有一个随机的Sleep,如果workAThread在调用parseData(pMyStruct)时需要Sleep很久,那么workBThread不是要等很久才有机会调用parseData(pMyStruct)?好象与实际的调用情况不符.

2、如果操作系统不提供互斥,那么假设parseData(pMyStruct)的函数体汇编以后的示意代码如下
proc parseData ;实现100+200+300,结果等于600
push ebx
.......
mov eax,DWORD[ebx+10]; ①假设盏内初试数据DWORD[ebx+10]等于100
add eax,200; ②
add eax,300; ③
mov DWORD[ebx+10],eax ④
.......
pop ebx
end
假设workAThread在执行完第②步之后(eax=300),CPU时间片被workBThread剥夺,workBThread在执行完第①步之后(eax=100)把CPU控制权交回workAThread,workAThread继续执行完函数体内剩下的部分包括③④,结果workAThread中的DWORD[ebx+10]为400,与理想结果600不符.workAThread在执行完函数体内剩下的部分代码后,CPU控制权转到workBThread继续执行②之后的语句.....

难道寄存器在各个线程里也是各有各的互不影响?

rageliu 2007-12-14
  • 打赏
  • 举报
回复
至于到底会不会复制两份代码 ?

不会。
栈压参后call addr
rageliu 2007-12-14
  • 打赏
  • 举报
回复
wang_junjie说的对。

就函数而言,其采用栈方式压参,所以参数不会有问题。这里需要注意的是函数内部,是否使用了全局变量,如果有,就不安全了
大熊猫侯佩 2007-12-14
  • 打赏
  • 举报
回复
//由于是多线程,我们可知workAThread和workAThread都有自己的线程运行空间,
//那么void parseData(MyStruct* pMyStruct)到底会不会复制两份代码
//到workAThread和workAThread各自的运行空间中,还是真的作为一个全局函数?

当然不会复制2分代码,永远只有一份该函数2进制指令码的内存拷贝
大熊猫侯佩 2007-12-14
  • 打赏
  • 举报
回复
关键在2个new处,一般来说new自身提供了你所说的线程安全,它确保多线程分配空间时不会出现所谓
的共享写关键内存数据结构的情况,即:

mov eax,var
add eax,1
mov var,eax


如果new提供了线程安全,那么你的全局函数也是安全的。
北方大冬瓜 2007-12-14
  • 打赏
  • 举报
回复
最关键看
void parseData(MyStruct* pMyStruct)
{
.........
}
函数的编写是不是线程安全的,多线程不同调用区别的只是数据段部分、代码段是共享的:如果仅仅使用栈空间的变量是线程安全的。
robotom 2007-12-14
  • 打赏
  • 举报
回复
>>于是多线程,我们可知workAThread和workAThread都有自己的线程运行空间,那么void parseData(MyStruct* pMyStruct)到底会不会复制两份代码到workAThread和workAThread各自的运....

不会复制两份代码.
这个函数的局部变量和参数放在Stack上,而两个线程拥有自己独立的Stack., 因此这个函数的代码在两个线程里运行时,会有各自独立的参数和局部变量.

ouyh12345 2007-12-14
  • 打赏
  • 举报
回复
全局函数使用了全局变量或静态变量时,需要互斥。
光看void parseData(MyStruct* pMyStruct)不能决定parseData是不是线程安全,
得看调用的方式,即pMyStruct参数会不会相同
jsphuang 2007-12-14
  • 打赏
  • 举报
回复
安全,
parseData(pMyStruct)的数据并没共享。

15,471

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC 进程/线程/DLL
社区管理员
  • 进程/线程/DLL社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧