赞同amo同志的意见,问题是出在IntToStr函数上。
分析出问题的原因,更赞同cmldy同志的意见,导致IntToStr函数出现问题的根
本原因在于它使用了Delphi内置的一个非常特殊的类型:String类型。(备注:
cmldy同志提出的例子可以正确运行)
特作以下程序,这个程序非常简单,但是因为使用了String类型,而会导致线程
发生错误。
function ThreadFunc3(p
ointer):LongInt;stdcall;
var
a:String;
l:integer;
begin
for l:=0 to 10000000do
begin
a:='Hello!';
a:=a+'你好!';
end;
end;
procedure TForm1.Button3Click(Sender: TObject);
var
hThread:THandle;
ThreadID
WORD;
i:integer;
begin
for i:=1 to 10do
//连续启动十个线程,只要启动线程超过两个就会发生错误
//如果仅仅启动一个线程,绝对不会发生错误
begin
hthread:=CreateThread(nil,
0,
@THreadFunc3,
nil,
0,
ThreadID);
if hthread=0 then
MessageBox(Handle,'NO Thread',nil,MB_OK)
else
closehandle(hthread);
end;
end;
上面的线程函数ThreadFunc3非常简单,仅仅是来回改变字符串a:string的数
值,一旦启动两个以上的线程,则会出错。
线程重入发生错误的原因一般是因为不同线程之间共享一共同资源,而因没有引
入同步访问此共同资源的同步控制器而导致发生错误。共同资源如:全局变量、全局
堆等。而局部变量(函数自己定义的简单变量)、局部栈等的访问则不会导致发生线
程重入的错误。
String类型我们可以对它做一些仔细的分析,String在Delphi中分三种子类型,
分别是ShortString、AnsiString(LongString)、WideString。这三种类型中,
ShortString属于一种简单的类型,是可以在函数(线程)局部堆栈内存取的。
而Delphi默认的String类型,是AnsiString类型,这个类型就是一个很复杂的类型。
AnsiString类型长度从4字节到2GB字节,是一个动态可以改变大小的类型。线程
的局部堆栈是难以储存实现的,对函数中a:string类型的内存实现,实际上函数线程
栈中仅仅保存String类型变量的地址。它的字符串存储空间则是在系统(全局堆)空
间生成的。
对字符串类型变量的付值,实际上是给变量付一个地址,而字符串类型变量的运
算,则就复杂很多,首先,系统调用@getleng算出长度,在调用@getmem在系统(全
局堆)空间申请mem,然后再调用@strcpy拷贝其他的字符串长度。(以上是通过观察
单步运行Delphi中的CPU汇编指令观察到的)
就是说,对String的大多数操作,都涉及到全局堆栈的操作。相对单线程的系统
来讲,使用String类型绝对不会出错,而多线程,则存在潜在的冲突的机会。特别是
大量的String类型运算,一般会引起系统堆竞争操作,则及有可能发生严重的存储冲
突,而导致系统崩溃。
概括上面的分析,就是String类型是线程不安全的,所有涉及到String类型的函
数也存在线程不安全的因素。对系统内存(全局堆)的操作,应该用同步机制访问方可。
上面的分析,仅仅代表XiDao(小刀)的个人意见,不代表Borland官方意见。
(hehe,小刀没有买正式版的Delphi,所以也没有办法获取Borland的帮助)仅仅供
同小刀一样使用盗版的朋友参考,所以可能存在很多错误,希望大家给予纠正。以后
小刀也会买正式版的Delphi了
小刀 2000.5.13