delphi多线程编程Word格式文档下载.docx
《delphi多线程编程Word格式文档下载.docx》由会员分享,可在线阅读,更多相关《delphi多线程编程Word格式文档下载.docx(21页珍藏版)》请在冰点文库上搜索。
(例程:
Tst_Thread.dpr)
在一个Form上放3个按钮和一个Memo,然后加上下面这段。
var
Form1:
TForm1;
m:
integer;
{$R
*.dfm}
function
Func1(const
n:
Integer):
Integer;
//
定义一个耗时函数来运行
Result:
=Round(abs(Sin(Sqrt(n))));
i:
for
i
:
0
to
20000000
do
inc(m,Func1(i));
//m全局变量
TForm1.Button1Click(Sender:
TObject);
TForm1.Button2Click(Sender:
MyThread:
TMyThead;
=0;
=TMyThead.Create(False);
TForm1.Button3Click(Sender:
Memo1.Lines.Add('
ok'
+inttostr(m));
end.
Button1Click后,Button3要过好几秒才能按下(嘿嘿,我的机子好,书上的例子i才200万,我加到2000万Button3才延迟几秒)。
Button2Click后,立刻可以按Button3,这就是线程的好处。
另外,我这里还没搞懂线程在哪安全free,所以干脆不free了。
注意:
当TThread的Create()被调用时,需要传递一个布尔型的参数CreateSuspended。
如果把这个参数设成False,那么当调用Create()后,Excute()会被自动地调用,也就是自动地执行线程代码。
如果该参数设为True,则需要运行TThread的Resume()来唤醒线程。
一般情况下,当你调用Create()后,还会有一些其他的属性要求设置。
所以,应当把CreateSuspended参数设为True,因为在TThread已执行的情况下设置TThread的属性可能会引起麻烦。
再深入一点讲,在构造函数Create()中隐含调用了一个RTL例程BeginThread(),而它又调用了一个API函数CreateThread()来创建一个线程对象的实例。
CreateSuspended参数表明是否传递CREATE_SUSPEDED标志给CreateThread()。
三、线程的安全Free:
(ps:
这是我从PDF上copy整理的,例子稍微改过,本人没有那么高深)
当线程对象的Excute()执行完毕,我们就认为此线程终止了。
这时,它会调用Delphi的一个标准例程EndThread(),这个例程再调用API函数ExitThread()。
由ExitThread()来清除线程所占用的栈。
当结束使用TThread对象时,应该确保已经把这个ObjectPascal对象从内存中清除了。
这才能确保所有内存占有都释放掉。
尽管在进程终止时会自动清除所有的线程对象,但及时清除已不再用的对象,可以使内存的使用效率提高。
利用将FreeOnTerminate的属性设为True的方法来及时清除线程对象是最方便的办法,这只需要在Excute()退出前设置就行了。
设置方法如下:
FreeOnTerminate:
=True;
这样,当一个线程终止时,就会触发OnTerminate事件,就有机会在事件处理过程内清除线程对象了。
若提前退出,Excute()就要不断检查Terminated属性的值。
上面的代码继续加上:
//终止后自动free
=0to
if
Terminated
then
Break;
某些紧急情况下,你可以使用Win32API函数TerminateThread()来终止一个线程。
但是,除非没有别的办法了,否则不要用它。
例如,当线程代码陷入死循环时。
TerminateThread()的声明如下:
functionTerminateThread(hThread:
THandle;
dwExitCode:
DWORD);
TThread的Handle属性可以作为第一个参数,因此,TerminateThread()常这样调用:
TerminateThread(MyThread.Handle,0)
如果选择使用这个函数,应该考虑到它的负面影响。
首先,此函数在WindowsNT与在
Windows95/98下并不相同。
在Windows95/98下,这个函数能够自动清除线程所占用的栈;
而在WindowsNT下,在进程被终止前栈仍然保留。
其次,无论线程代码中是否有try...finally块,这个函数都会使线程立即停止执行。
这意味着,被线程打开的文件没有被关闭、由线程申请的内存没有被释放等情况。
而且,这个函数在终止线程的时候也不通知DLL,当DLL关闭时,这也容易出现问题。
四、线程的挂起和唤醒:
当线程Create()中的CreateSuspended属性为True时,线程创建后并不立即执行。
可以用用Suspend()和Resume()来动态地挂起或唤醒。
//挂起和唤醒
Func2(MyThread:
Memo:
TMemo);
PassTime:
Cardinal;
MyThread.Suspend;
=GetTickCount;
Memo.Lines.Add('
'
Sleep(2000);
//等待2秒
=GetTickCount-PassTime;
SuspendTime:
+inttostr(PassTime)+'
*m:
MyThread.Resume;
ReSumeTime:
TForm1.Button5Click(Sender:
Func2(MyThread,Memo1);
运行结果:
Memo1
2000*m:
9630399
五、取得线程的时间:
(本节只是介绍GetThreadTimes()的用法,可略过)
上例可以看见,用Windows.GetTickCount()来取得线程运行时间是不准确的。
Win32提供了一个API函数GetThreadTimes(),定义如下:
BOOLWINAPIGetThreadTimes(
HANDLEhThread,
LPFILETIMElpCreationTime,线程创建的时间
LPFILETIMElpExitTime,线程退出的时间。
如果线程还在执行,此值无意义。
LPFILETIMElpKernelTime,执行操作系统代码所用的时间。
LPFILETIMElpUserTime执行应用程序本身代码所用的时间。
);
函数返回值失败时为0,成功为不等于零的数。
可用GetLastError()来取得更详细的资料。
以上四个参数都是TFileTime类型。
此类型在Windows单元中声明如下:
typedefstruct_FILETIME{
DWORDdwLowDateTime;
DWORDdwHighDateTime;
}64位数,以100纳秒(1纳秒=10亿分之一秒)的时间间隔自1601年1月1号(UTC)表示。
TFileTime的长度是64位,为了进行数学运算可以把它转换为Int64。
例如两个TFileTime的值比较大小:
IfInt64(UserTime)>
Int64(KernelTime)thenBeep;
Delphi只提供了FileTimeToDosDateTime,FileTimeToLocalFileTime和FileTimeToSystemTime这三个转换函数,所以要自己写和TdateTime的转换函数。
上面的例子加多一个函数:
Func3(MyThread:
//计算线程时间
FileTimeToDateTime(FileTime:
TFileTime):
TDateTime;
//TFileTime转化成TDateTime
SysTime:
TSystemTime;
not
FileTimeToSystemTime(FileTime,SysTime)
then
Raise
Exception.CreateFmt('
FileTimeToSystemTime
failed.'
+
Error
code
%d'
[GetLastError]);
with
SysTime
=EncodeDate(wYear,wMonth,wDay)+
EncodeTime(wHour,wMinute,wSecond,wMilliseconds);
CreateTime,ExitTime,KernelTime,UserTime:
TFileTime;
GetThreadTimes(MyThread.Handle,CreateTime,ExitTime,KernelTime,UserTime)
创建时间:
+DateTimeToStr(FileTimeToDateTime(CreateTime)));
退出时间:
+DateTimeToStr(FileTimeToDateTime(ExitTime)));
Win时间:
+DateTimeToStr(FileTimeToDateTime(KernelTime)));
进程时间:
+DateTimeToStr(FileTimeToDateTime(UserTime)));
---------------'
Func3(MyThread,Memo1);
9585649
---------------
2008-10-1019:
59:
31
1601-1-1
1601-1-223:
58
还是不清楚如何具体运用。
◆delphi多线程编程之二◆
Delphi2007+WinXPsp3例程:
Tst_Thread2.dpr)
一、线程的局部变量threadvar
type
TForm1
class(TForm)
Button1:
TButton;
Memo1:
TMemo;
Button1Click(Sender:
public
Public
TMyThread=class(TThread)
FNewStr:
string;
constructor
Create(const
ANewStr:
string);
threadvar
GlobalStr:
SetShowStr(const
S:
s='
//MessageBox(0,pchar(GlobalStr),'
The
String
is...'
MB_OK)
inc(n);
Memo.Lines.Add(inttostr(n)+GlobalStr);
end
else
=S;
TMyThread.Create(const
=ANewStr;
Inherited
Create(False);
TMyThread.Execute;
SetShowStr(FnewStr,
Form1.Memo1);
SetShowStr('
=1;
Hello
World'
//Global:
='
//show
1Global
TMyThread.Create('
Mygodsos'
show
2Global
Sleep(100);
3Global
end.
当GlobalStr声明不同时,结果分别是:
Threadvarvar
1HelloWorld1HelloWorld
3HelloWorld3Mygodsos
2Mygodsos2Mygodsos
Delphi利用关键字threadvar封装API线程局部存储。
它能使你在第一个运行的线程中创建一个全局变量的拷贝。
如果用ThreadVar声明变量,则在程序结束前必须手动释放其占用的空间(这个手动释放的问题不知道d2007解决没有?
)
(ps我看到很多关于threadvar释放要:
='
的,若不是string类型的给如何释放?
二、双线程看看Threadvar:
把Execute()里Create(False)改成Create(True)
MyThread1:
TMyThread;
MyThread2:
Form1.Memo1);
Global
=TMyThread.Create('
thread
1:
2:
MyThread1.Resume;
MyThread2.Resume;
当GlobalStr声明为不同,结果对比是:
ThreadvarVar
4HelloWorld4thread2:
2thread1:
3thread2:
3thread2:
2thread1:
这里出现一个十分有趣的问题,若sleep()位置不同,结果不一样。
//Sleep(100);
改成这样后,结果是:
3HelloWorld3thread1:
4thread2:
4thread2:
留意一下前面的序号,似乎sleep()的作用比较奇怪。
三、Sleep()函数
Win32API过程Sleep()。
此过程声明如下:
procedureSleep(dwMilliseconds:
stdcall;
Sleep()过程用来告诉操作系统,当前的线程在参数dwMilliseconds指定的时间内不需要分配任何CPU时间。
插入这个调用是使很多的任务在发生时,使执行哪个线程有一些随机性。
通常,可以把参数dwMilliseconds设为0。
尽管,这并没有使当前的线程真的“睡眠”,但它使操作系统把CPU时间分给了其他优先级相等或更高的线程。
要小心Sleep()神秘的时间调整问题。
Sleep()可能会使你的机器出现特别的问题。
这种问题在另一台机器上可能无法再现。
对上面的例子,改回以下,把global设成var声明:
执行几次,结果:
看来sleep后,在主进程中先返回哪个线程是有一定的随机性的。
但前面的序号还是一样的,意思是虽然主进程返回哪个线程的次序不一样,但线程执行的次序还是没变。
不知道我这样理解对不对。
◆Delphi多线程编程之三同步读写全局数据◆
Tst_Thread3.dpr)
开始研究最重要的多线程读写全局数据了,结合书上的例子,我修改成下面的情况:
Tst_Thread3U;
uses
Windows,
Messages,
SysUtils,
Variants,
Classes,
Graphics,
Controls,
Forms,Dialogs,
StdCtrls;
cl