• 多线程编程(1) - 先入门


    image

    先编写一个循环 50000 次的程序,每次在程序界面左上方(10,10)的位置输出数字,代码如下:

    复制代码
      1 procedure TForm1.btn2Click(Sender: TObject);
      2 var
      3   i: Integer;
      4 begin
      5   for i := 0 to 500000 do
      6   begin
      7     Canvas.TextOut(10, 10, IntToStr(i));
      8   end;
      9 end;
    复制代码

    上面程序运行时, 在程序运行期间拖动窗体,窗体基本是 卡"死" 的。

    解决卡死方法1(Application.ProcessMessages)

    一个简单的办法( Application.ProcessMessages )来解决这个问题,代码如下:

    复制代码
      1 procedure TForm1.btn2Click(Sender: TObject);
      2 var
      3   i: Integer;
      4 begin
      5   for i := 0 to 500000 do
      6   begin
      7     Canvas.TextOut(10, 10, IntToStr(i));
      8     Application.ProcessMessages; // 解决方法之一
      9   end;
     10 end;
    复制代码

    这个 Application.ProcessMessages  它会检查并先处理消息队列中的其他消息.
    但这算不上多线程, 运行中拖动窗体, 你会发现循环会暂停下来...

    在 Delphi 中使用多线程有两种方法:

    1. 调用 API
    2. 使用 TThread 类

    解决卡死方法2(调用 API)

    复制代码
      1 function MyTest(p: Pointer): Integer; stdcall;
      2 var
      3   i: Integer;
      4 begin
      5   for i := 0 to 500000 do
      6   begin
      7     Form1.Canvas.Lock; // 在 Canvas 中 Lock ,其他访问先暂停
      8     Form1.Canvas.TextOut(10, 10, IntToStr(i));
      9     Form1.Canvas.Unlock; // 用完了,解锁
     10   end;
     11   Result := 0;
     12 end;
     13 
     14 procedure TForm1.btn3Click(Sender: TObject);
     15 var
     16   ID: Cardinal;
     17 begin
     18   CreateThread(nil, 0, @MyTest, nil, 0, ID);
     19 end;
    复制代码

    CreateThread 要使用的函数是系统级的,不能是某个类的方法,必须有严格的格式(参数、返回值)要求,还必须用上 stdcall 是协调参数顺序的,虽然这里只有一个参数没有顺序可言,但这是使用系统函数的惯例。

    CreateThread 还需要一个 var 参数来接收新建线程的 ID,在 Delphi 10.3.3 中为 Cardinal 类型。

    解决卡死方法3(使用 TThread 类)

    TThread 类有一个抽象方法(Execute),抽象类只能被继承使用,下面继承为 TMyThread,主要是实现抽象方法 Execute,等我们实例化 TMyThread 后,首先会执行 Execute 方法中的代码。

    复制代码
      1 type
      2   TMyThread = class(TThread)
      3   protected
      4     procedure Execute; override;
      5   end;
      6 
      7 implementation
      8 
      9 {$R *.dfm}
     10 { TMyThread }
     11 
     12 procedure TMyThread.Execute;
     13 var
     14   i: Integer;
     15 begin
     16   inherited;
     17   FreeOnTerminate := True;
     18   for i := 0 to 500000 do
     19   begin
     20     Form1.Canvas.Lock;
     21     Form1.Canvas.TextOut(10, 10, IntToStr(i));
     22     Form1.Canvas.Unlock;
     23   end;
     24 end;
     25 
     26 procedure TForm1.btn1Click(Sender: TObject);
     27 var
     28   MyThread: TMyThread;
     29 begin
     30   MyThread := TMyThread.Create(True);
     31   MyThread.Resume;
     32 
     33   //TMyThread.Create(False); 也可以这样实例化
     34 
     35   //with TMyThread.Create(True) do Resume; 还可以这样实例化
     36 end;
    复制代码

    在上面代码中,实例化用到的 MyThread 变量,毫无用处,可以直接

     TMyThread.Create(False);

    执行或者

    with TMyThread.Create(True) do Resume;

    线程建立后不会立即调用 Execute,可以在需要的时候用 Resume 方法执行线程。

    在 TThread 类的例子中,还有一个这样的句子:

    FreeOnTerminate := True;

    由于 TThread 的特殊性,很多时候我们不能确定线程什么时候执行完毕,因此不能 Free,如果 FreeOnTerminate 为 True,线程执行完毕后自动释放。

  • 相关阅读:
    2022-32周 项目问题整理
    Linux安装Whisper-Jax
    2022微服务面试题 最新50道题(含答案解析)
    CDN加速服务:网站快递服务
    Leetcode: 63. 不同路径 II(动态规划)
    【Java核心知识】ThreadLocal相关知识
    1.1 大数据简介-hadoop-最全最完整的保姆级的java大数据学习资料
    Ruby线程安全秘籍:深入探索并发编程的隐秘角落
    【路径规划】如何给路径增加运动对象
    Chewy 2023年9月 面经和题目以及总结
  • 原文地址:https://www.cnblogs.com/pchmonster/p/15933998.html