• delphi多线程编程之一create和Free◆


    Google搜到线程的例子都是那个画图的,猛禽那个多线程又太过高深(对于我这一滴水来说),万一老师开线程的博还是要等。只有自己看着《Delphi5开发人员指南》中文版PDF一步一步来弄懂些初步的东西,到时候可以跟上万一老师的课程。

    一、创建:

    1、直接书写:

    1. unit Unit1;
    2. interface
    3. uses  Classes;
    4. TMyThead = class(TThread)
    5. private
    6. { Private declarations }
    7. protected
    8. procedure Execute; override;
    9. end;
    10. implementation
    11. {用鼠标放在上面的TMyThead上按ctrl+alt+c直接自动生成下面的}
    12. procedure TMyThead.Execute;
    13. begin
    14. inherited;
    15. end;

    2、在File菜单的New—Others—Delphi Files里面选Thread Object,出来一个对话框,你在Thread名字里填TMyThread后,就会自动生成一个新的Unit2,里面的内容和上面一样。

    二、简单例子:(例程:Tst_Thread.dpr)

    在一个Form上放3个按钮和一个Memo,然后加上下面这段。

    1. TMyThead = class(TThread)
    2. private
    3. { Private declarations }
    4. protected
    5. procedure Execute; override;
    6. end;
    7. var
    8.   Form1: TForm1;
    9.   m:integer;
    10. implementation
    11. {$R *.dfm}
    12. { TMyThead }
    13. function Func1(const n:Integer):Integer; // 定义一个耗时函数来运行
    14. begin
    15.   Result:=Round(abs(Sin(Sqrt(n))));
    16. end;
    17. procedure TMyThead.Execute;
    18. var
    19.   i:integer;
    20. begin
    21. for i := 0 to 20000000 do
    22.     inc(m,Func1(i)); //m全局变量
    23. end;
    24. procedure TForm1.Button1Click(Sender: TObject);
    25. var
    26.   i:integer;
    27. begin
    28. for i := 0 to 20000000 do
    29.     inc(m,Func1(i));  //m全局变量
    30. end;
    31. procedure TForm1.Button2Click(Sender: TObject);
    32. var
    33.   MyThread:TMyThead;
    34. begin
    35.   m:=0;
    36.   MyThread:=TMyThead.Create(False);
    37. end;
    38. procedure TForm1.Button3Click(Sender: TObject);
    39. begin
    40.   Memo1.Lines.Add('ok'+inttostr(m));
    41. end;
    42. 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对象时,应该确保已经把这个Object Pascal对象从内存中清除了。这才能确保所有内存占有都释放掉。尽管在进程终止时会自动清除所有的线程对象,但及时清除已不再用的对象,可以使内存的使用效率提高。利用将FreeOnTerminate的属性设为True的方法来及时清除线程对象是最方便的办法,这只需要在Excute()退出前设置就行了。设置方法如下:

    1. procedure TMyThead.Execute;
    2. var
    3.   i:integer;
    4. begin
    5.   FreeOnTerminate:=True;
    6. for i := 0 to 20000000 do
    7.     inc(m,Func1(i));
    8. end;

    这样,当一个线程终止时,就会触发OnTerminate事件,就有机会在事件处理过程内清除线程对象了。

    若提前退出,Excute()就要不断检查Terminated属性的值。上面的代码继续加上:

    1. procedure TMyThead.Execute;
    2. var
    3.   i:integer;
    4. begin
    5.   FreeOnTerminate:=True; //终止后自动free
    6. for i := 0 to 20000000 do
    7. begin
    8. if Terminated then Break;
    9.     inc(m,Func1(i));
    10. end;
    11. end;

    注意:某些紧急情况下,你可以使用Win32API函数TerminateThread()来终止一个线程。但是,除非没有别的办法了,否则不要用它。例如,当线程代码陷入死循环时。

    TerminateThread()的声明如下:

    function TerminateThread(hThread:THandle;dwExitCode:DWORD);

    TThread的Handle属性可以作为第一个参数,因此,TerminateThread()常这样调用:

    TerminateThread(MyThread.Handle,0)

    如果选择使用这个函数,应该考虑到它的负面影响。首先,此函数在Windows NT与在

    Windows 95/98下并不相同。在Windows 95/98 下,这个函数能够自动清除线程所占用的栈;而在Windows NT下,在进程被终止前栈仍然保留。其次,无论线程代码中是否有try...fina lly块,这个函数都会使线程立即停止执行。这意味着,被线程打开的文件没有被关闭、由线程申请的内存没有被释放等情况。而且,这个函数在终止线程的时候也不通知DLL,当DLL关闭时,这也容易出现问题。

    四、线程的挂起和唤醒:

    当线程Create()中的CreateSuspended属性为True时,线程创建后并不立即执行。可以用用Suspend()和Resume()来动态地挂起或唤醒。

    1. //挂起和唤醒
    2. procedure Func2(MyThread:TMyThead;Memo:TMemo);
    3. var
    4.   PassTime:Cardinal;
    5. begin
    6.   MyThread.Suspend;
    7.   PassTime:=GetTickCount;
    8.   Memo.Lines.Add('m:'+inttostr(m));
    9.   Sleep(2000);  //等待2秒
    10.   PassTime:=GetTickCount-PassTime;
    11.   Memo.Lines.Add('SuspendTime:'+inttostr(PassTime)+'*m:'+inttostr(m));
    12.   MyThread.Resume;
    13.   PassTime:=GetTickCount;
    14.   Sleep(2000);
    15.   PassTime:=GetTickCount-PassTime;
    16.   Memo.Lines.Add('ReSumeTime:'+inttostr(PassTime)+'*m:'+inttostr(m));
    17. end;
    18. procedure TForm1.Button5Click(Sender: TObject);
    19. var
    20.   MyThread:TMyThead;
    21. begin
    22.   m:=0;
    23.   MyThread:=TMyThead.Create(False);
    24.   Func2(MyThread,Memo1);
    25. end;

    运行结果:

    Memo1

    m:0

    SuspendTime:2000*m:0

    ReSumeTime:2000*m:9630399

    五、取得线程的时间:(本节只是介绍GetThreadTimes()的用法,可略过)

    上例可以看见,用Windows.GetTickCount()来取得线程运行时间是不准确的。Win32提供了一个API函数GetThreadTimes(),定义如下:

    BOOL WINAPI GetThreadTimes(

    HANDLE hThread,

    LPFILETIME lpCreationTime, 线程创建的时间

    LPFILETIME lpExitTime, 线程退出的时间。如果线程还在执行,此值无意义。

    LPFILETIME lpKernelTime, 执行操作系统代码所用的时间。

    LPFILETIME lpUserTime 执行应用程序本身代码所用的时间。

    );

    函数返回值失败时为0,成功为不等于零的数。可用GetLastError()来取得更详细的资料。

    以上四个参数都是TFileTime类型。此类型在Windows单元中声明如下:

    typedef struct _FILETIME {

    DWORD dwLowDateTime;

    DWORD dwHighDateTime;

    } 64位数,以100纳秒(1纳秒=10亿分之一秒)的时间间隔自1601年1月1号(UTC)表示。

    TFileTime的长度是64位,为了进行数学运算可以把它转换为Int64。例如两个TFileTime的值比较大小:

    If Int64(UserTime)> Int64(KernelTime) then Beep;

    Delphi只提供了FileTimeToDosDateTime,FileTimeToLocalFileTime和FileTimeToSystemTime这三个转换函数,所以要自己写和TdateTime的转换函数。

    上面的例子加多一个函数:

    1. procedure Func3(MyThread:TMyThead;Memo:TMemo); //计算线程时间
    2. function FileTimeToDateTime(FileTime:TFileTime):TDateTime; //TFileTime转化成TDateTime
    3. var
    4.     SysTime:TSystemTime;
    5. begin
    6. if not FileTimeToSystemTime(FileTime,SysTime) then
    7.       Raise Exception.CreateFmt('FileTimeToSystemTime failed.'+
    8. 'Error code %d',[GetLastError]);
    9. with SysTime do
    10.       Result:=EncodeDate(wYear,wMonth,wDay)+
    11.         EncodeTime(wHour,wMinute,wSecond,wMilliseconds);
    12. end;
    13. var
    14.   CreateTime,ExitTime,KernelTime,UserTime:TFileTime;
    15. begin
    16. if GetThreadTimes(MyThread.Handle,CreateTime,ExitTime,KernelTime,UserTime) then
    17. begin
    18.     Memo.Lines.Add('创建时间:'+DateTimeToStr(FileTimeToDateTime(CreateTime)));
    19.     Memo.Lines.Add('退出时间:'+DateTimeToStr(FileTimeToDateTime(ExitTime)));
    20.     Memo.Lines.Add('Win时间:'+DateTimeToStr(FileTimeToDateTime(KernelTime)));
    21.     Memo.Lines.Add('进程时间:'+DateTimeToStr(FileTimeToDateTime(UserTime)));
    22. end;
    23. end;
    24. procedure TForm1.Button5Click(Sender: TObject);
    25. var
    26.   MyThread:TMyThead;
    27. begin
    28.   m:=0;
    29.   MyThread:=TMyThead.Create(False);
    30.   Func2(MyThread,Memo1);
    31.   Memo1.Lines.Add('---------------');
    32.   Func3(MyThread,Memo1);
    33. end;

    运行结果:

    Memo1

    m:0

    SuspendTime:2000*m:0

    ReSumeTime:2000*m:9585649

    ---------------

    创建时间:2008-10-10 19:59:31

    退出时间:1601-1-1

    Win时间:1601-1-1

    进程时间:1601-1-2 23:59:58

    还是不清楚如何具体运用。

  • 相关阅读:
    哈工大《机器学习》最小二乘法曲线拟合——实验一
    最小二乘法曲线拟合以及matlab实现
    Dubbo简介
    Redis持久化策略
    Linux安装Redis
    RabbitMQ-Demo
    RabbitMQ安装相关
    SpringCloud-Alibaba-Nacos-Demo
    Nacos_启动失败原因
    IDEA中properties中文显示乱码
  • 原文地址:https://www.cnblogs.com/94YY/p/2199341.html
Copyright © 2020-2023  润新知