zoukankan      html  css  js  c++  java
  • delphi 线程教学第二节:在线程时空中操作界面(UI)

    转载自:https://www.cnblogs.com/lackey/p/6296414.html

    第二节:在线程时空中操作界面(UI)
     
    1.为什么要用 TThread ?
     
    TThread 基于操作系统的线程函数封装,隐藏了诸多繁琐的细节。
    适合于大部分情况多线程任务的实现。这个理由足够了吧?
    什么?你要用 windows 的线程 api 来实现多线程?
    我可以负责任地告诉你,如果你用 api 来实现多线程任务,
    加之你天资聪明,对 delphi 的面向对象思想掌握得非常快,
    那么最终也你也会写一个与 TThread 类似的东西来提高开发效率。
    何必折腾呢?要相信 delphi 的工程师,人家早已看透了一切。咳咳。
    同理,要相信微软的工程师,windows 操作系统是没有啥大问题的。
    更同理,要相信设计手机的工程师,不需要贴膜,人家好不容易把才手机变薄的。
    哈哈,扯远了。。。
    (本教程默认操作系统为 windows 7/10 , delphi 的版本为 XE8,大多数代码均能在 XE2 上运行)
     
    2.线程时空中操作界面(UI)到底有什么门道?
     
    很多教程中都一再强调,线程时空里,不准直接去更新 UI ,但似乎没有说明原因。
    我们假设UI 界面允许多个线程同时去更新,看看会发生什么情况。
    如果两个线程,同时都在界面相同的区域进行画图操作,比如一个要画绿色,一个要红色,
    那么最终,界面上是不是可能出现一个大花脸?
    可以这样朴实地理解,就知道为什么 UI 不允许多线程去操作了。不是不能,是不得已。
    (线程中不允许直接操作 UI,在安卓下同样适用)
     
    3. TThread.Synchronize() 原理。
     
    是用 SendMessage 函数,发了一个 WM_NULL 消息给窗口。
    窗口接到消息后再去更新界面。窗口消息响应事件可以理解为主线程时空。
     
    以下是接上节的实例,来看如何正确地显示计算结果在窗口上。
     
    unit Unit10;
    interface
    uses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, uAccumulation, Vcl.StdCtrls;
    type
      TForm10 = class(TForm)
        Edit1: TEdit;
        Button1: TButton;
        procedure Button1Click(Sender: TObject);
      private
        procedure OnAccumulated(Sender: TAccumulationThread);
      end;
     
    implementation
    {$R *.dfm}
     
    procedure TForm10.Button1Click(Sender: TObject);
    var
      accThread: TAccumulationThread;
    begin
      accThread := TAccumulationThread.Create(true);
      accThread.OnAccumulated := self.OnAccumulated; //指定事件。
      accThread.FreeOnTerminate := true// 线程结束后自动释放
      accThread.Num := 100;
      accThread.Start;
    end;
     
    procedure TForm10.OnAccumulated(Sender: TAccumulationThread);
    begin
      // 这里是线程时空
      // 要更新 UI ,要用 Synchorinize 把更新的操作
      // 塞到主线程时空里去运行。注意理解:“塞!”
      TThread.Synchronize(nil,
        procedure
        begin
          // 这里的代码被塞到主线程时空里去了。
          Edit1.Text := inttostr(Sender.Total);
        end);
      // Synchronize 第一个参数是 nil
      // 第二个参数是一个匿名函数 什么是匿名函数? 以后会介绍到。
    end;
    end.
     
    unit uAccumulation;
    interface
    uses
      Classes;
    type
      TAccumulationThread = class; //此为提前申明
      TOnAccumulated = procedure(Sender: TAccumulationThread) of object;
      // 如果不提前申明,Sender 就要定义成 TObject
      // 在事件函数中,要操作 Sender 就需要强制转换
      TAccumulationThread = class(TThread)
      protected
        procedure Execute; override;
      public
        Num: integer;
        Total: integer;
        OnAccumulated: TOnAccumulated;
      end;
     
    implementation
     
    procedure TAccumulationThread.Execute;
    var
      i: integer;
    begin
      Total := 0;
      if Num > 0 then
      begin
        for i := 1 to Num do
          Total := Total + i
      end;
      // 当计算完成后,就调用  OnAccumulated 通知调用者
      if Assigned(OnAccumulated) then
        OnAccumulated(self);
    end;
    end.
    

      

     
     4. 哪些代码运行在线程时空?
     
    Execute 函数中运行的、调用的代码,都是”线程代码“。与代码书写位置无关!!!
    Sysnchronize 是个特殊的存在,它可以在线程时空里,把代码塞到主线程时空里去运行。
     
    第三节,将实现线程如何保持生命力,创建后可以反复使用。慢慢进入实用阶段了,请不要错过。
  • 相关阅读:
    原子变量AtomicInteger
    8.2.2.2 Speed of UPDATE Statements 加速UPDATE语句:
    8.2.2 Optimizing DML Statements 优化DML 语句:
    CSS3中-moz、-ms、-webkit和-o分别代表什么意思
    8.2.1.19 Optimizing LIMIT Queries 优化LIMIT 查询:
    java.lang.IllegalArgumentException: Negative time
    8.2.1.17 DISTINCT Optimization
    Python基本语法_函数_参数的多类型传值
    Python基本语法_函数_参数的多类型传值
    8.2.1.16 GROUP BY Optimization
  • 原文地址:https://www.cnblogs.com/approx/p/11852138.html
Copyright © 2011-2022 走看看