• delphi 对TThread扩充TSimpleThread


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

    对线程的使用,是每个开发者都应该熟练掌握的,也是进阶的重要一环。

    可以这样说,没有线程,连界面假死的问题都解决不了,就更别谈并行处理来提高效率了。

    本例对线程进行改进,打造一个基础的线程,以后线程应用都从此类继承,大大节省了代码,提高了效率。

    经长期实践,此代码能够应付许多情况,值得一学。

    它的应用1:TReadHtmlThread (读网页)

    它的应用2: TElegantThread (把多个线程的请求阻塞到另一个线程)

    它的应用3: TThreadTimer 多线程 Timer 

    unit uSimpleThread;
    interface
    uses
      System.Classes, System.SysUtils, System.SyncObjs;
    
    type
    
      // 显示信息,调用方法 DoOnStatusMsg(AMsg);
      TOnStatusMsg = procedure(AMsg: string) of object;
    
      // 显示调试信息,一般用于显示出错信息,用法 DoOnDebugMsg(AMsg);
      TOnDebugMsg = TOnStatusMsg;
    
      TSimpleThread = class(TThread)
      public type // "执行过程"的类别定义
    
        TGeneralProc = procedure; // 普通的,即 procedure DoSomeThing;
        TObjectProc = procedure of object; // 类的,即 TXxxx.DoSomeThign; 用得多
        TAnonymousProc = reference to procedure; // 匿名的
      private type
        TProcKind = (pkGeneral, pkObject, pkAnonymous); // "执行过程"的类别
      private
    
        FGeneralProc: TGeneralProc;
        FObjProc: TObjectProc;
        FAnoProc: TAnonymousProc;
    
        FProcKind: TProcKind;
    
        FEvent: TEvent; // 用于阻塞,它是一个信号量
        FActiveX: boolean; // 是否在线程中支持 Com ,如果你要在线程中访问 IE 的话,就设定为 True
    
        FOnStatusMsg: TOnStatusMsg;
        FOnDebugMsg: TOnDebugMsg;
    
        FTagID: integer; // 给线程一个代号,在线程池的时候用来作区别
        FParam: integer; // 给线程一个参数,方便识别
    
        procedure SelfStart; // 触发线程运行
    
        procedure DoExecute; // 这个函数里面运行的代码是“线程空间”
        procedure DoOnException(e: exception); // 异常信息显示 调用 DoOnDebugMsg(AMsg);
    
        procedure SetTagID(const Value: integer);
        procedure SetParam(const Value: integer);
    
        procedure SetOnStatusMsg(const Value: TOnStatusMsg);
        procedure SetOnDebugMsg(const Value: TOnDebugMsg);
    
      protected
    
        FWaitStop: boolean; // 结束标志,可以在继承类中使用它,以确定线程是否停止运行
    
        procedure DoOnStatusMsg(AMsg: string); // 显示普通信息
        procedure DoOnDebugMsg(AMsg: string); // 显示调式信息
    
        procedure Execute; override; // 重载 TThread.Execute
    
        procedure OnThreadProcErr(e: exception); virtual; // 异常发生事件
    
        procedure WaitThreadStop; // 等待线程结束
    
        procedure BeforeExecute; virtual; // 看名字,不解释
        Procedure AfterExecute; virtual; // 看名字,不解释
    
        procedure SleepExceptStopped(ATimeOut: Cardinal); // 这个高大上了,要解释一下。
        { 有时线程没有任务时,就会休息一会儿,但是,休息的时候,可能会接收到退出线程的指令
          此函数就是在休息的时候也检查一下停止指令
        }
    
      public
    
        // 改变一下 Create 的参数,AllowedActiveX:是否允许线程代码访问 Com
        constructor Create(AllowedActiveX: boolean = false); reintroduce;
    
        destructor Destroy; override;
    
        procedure ExeProcInThread(AProc: TGeneralProc); overload; // 这三个,对外的接口。
        procedure ExeProcInThread(AProc: TObjectProc); overload;
        procedure ExeProcInThread(AProc: TAnonymousProc); overload;
    
        procedure StartThread; virtual;
        { 启动线程,一般只调用一次。
          以后就由线程的响应事件来执行了
        }
    
        procedure StopThread; virtual; // 停止线程
    
        property OnStatusMsg: TOnStatusMsg read FOnStatusMsg write SetOnStatusMsg;
        property OnDebugMsg: TOnDebugMsg read FOnDebugMsg write SetOnDebugMsg;
        property WaitStop: boolean read FWaitStop;
        property TagID: integer read FTagID write SetTagID;
        property Param: integer read FParam write SetParam;
    
      end;
    
    implementation
    
    uses
      ActiveX;
    
    procedure TSimpleThread.AfterExecute;
    begin
    end;
    
    procedure TSimpleThread.BeforeExecute;
    begin
    end;
    
    constructor TSimpleThread.Create(AllowedActiveX: boolean);
    var
      BGUID: TGUID;
    begin
      inherited Create(false);
      FActiveX := AllowedActiveX;
      FreeOnTerminate := false; // 我们要手动Free线程
      CreateGUID(BGUID);
      FEvent := TEvent.Create(nil, true, false, GUIDToString(BGUID));
    end;
    
    destructor TSimpleThread.Destroy;
    begin
      StopThread; // 先停止
      WaitThreadStop; // 再等待线程停止
      {
        在继承类的 Destroy 中,也要写上这两句. 如:
        暂时未找到更好的办法,这点代码省不了
        destructor TXXThread.Destroy;
        begin
        StopThread;
        WaitThreadStop;
        xxx.Free;
        Inherited;
        end;
      }
      FEvent.Free;
      inherited;
    end;
    
    procedure TSimpleThread.DoExecute; // 此函数内执行的代码,就是在多线程空间里运行
    begin
      BeforeExecute;
      repeat
    
        FEvent.WaitFor;
        FEvent.ResetEvent; // 下次waitfor 一直等
        { 这里尝试了很多些,总 SelfStart 觉得有冲突,经过多次修改并使用证明,
          没有必要在这里加锁,因为只调用 startThread 一次,剩下的交给线程影应事件
        }
    
        if not Terminated then // 如果线程需要退出
        begin
    
          try
    
            case FProcKind of
              pkGeneral: FGeneralProc;
              pkObject: FObjProc;
              pkAnonymous: FAnoProc;
            end;
    
          except
    
            on e: exception do
            begin
              DoOnException(e);
            end;
    
          end;
    
        end;
    
      until Terminated;
      AfterExecute;
      //代码运行到这里,就表示这个线程不存在了。再也回不去了,必须释放资源了。
    end;
    
    procedure TSimpleThread.DoOnDebugMsg(AMsg: string);
    begin
      if Assigned(FOnDebugMsg) then
        FOnDebugMsg(AMsg);
    end;
    
    procedure TSimpleThread.DoOnException(e: exception);
    var
      sErrMsg: string;
    begin
      sErrMsg := 'ClassName:' + ClassName + #13#10;
      sErrMsg := sErrMsg + 'TagID:' + IntToStr(FTagID) + #13#10;
      sErrMsg := sErrMsg + 'Param:' + IntToStr(Param) + #13#10;
      sErrMsg := sErrMsg + 'ErrMsg:' + e.Message + #13#10;
      DoOnDebugMsg(sErrMsg);
      OnThreadProcErr(e);
    end;
    
    procedure TSimpleThread.DoOnStatusMsg(AMsg: string);
    begin
      if Assigned(FOnStatusMsg) then
        FOnStatusMsg(AMsg);
    end;
    
    procedure TSimpleThread.Execute;
    begin
      //是否支持 Com
      if FActiveX then
      begin
        CoInitialize(nil);
        try
          DoExecute;
        finally
          CoUninitialize;
        end;
      end
      else
        DoExecute;
    end;
    
    procedure TSimpleThread.ExeProcInThread(AProc: TGeneralProc);
    begin
      FGeneralProc := AProc;
      FProcKind := pkGeneral;
      SelfStart;
    end;
    
    procedure TSimpleThread.ExeProcInThread(AProc: TObjectProc);
    begin
      FObjProc := AProc;
      FProcKind := pkObject;
      SelfStart;
    end;
    
    procedure TSimpleThread.ExeProcInThread(AProc: TAnonymousProc);
    begin
      FAnoProc := AProc;
      FProcKind := pkAnonymous;
      SelfStart;
    end;
    
    procedure TSimpleThread.OnThreadProcErr(e: exception);
    begin;
    end;
    
    procedure TSimpleThread.SelfStart;
    begin
      //经常多次尝试,最终写成这样,运行没有问题
      if FEvent.WaitFor(0) <> wrSignaled then
        FEvent.SetEvent; // 让waitfor 不再等
    end;
    
    procedure TSimpleThread.StopThread;
    begin
      //继承类的代码中,需要检查 FWaitStop ,来控制线程结束
      FWaitStop := true;
    end;
    
    procedure TSimpleThread.SetOnDebugMsg(const Value: TOnDebugMsg);
    begin
      FOnDebugMsg := Value;
    end;
    
    procedure TSimpleThread.SetOnStatusMsg(const Value: TOnStatusMsg);
    begin
      FOnStatusMsg := Value;
    end;
    
    procedure TSimpleThread.SetParam(const Value: integer);
    begin
      FParam := Value;
    end;
    
    procedure TSimpleThread.SetTagID(const Value: integer);
    begin
      FTagID := Value;
    end;
    
    procedure TSimpleThread.SleepExceptStopped(ATimeOut: Cardinal);
    var
      BOldTime: Cardinal;
    begin
      // sleep 时检测退出指令,以确保线程顺序退出
      // 多个线程同时工作,要保证正确退出,确实不容易
      BOldTime := GetTickCount;
      while not WaitStop do
      begin
        sleep(50);
        if (GetTickCount - BOldTime) > ATimeOut then
          break;
      end;
    end;
    
    procedure TSimpleThread.StartThread;
    begin
      FWaitStop := false;
    end;
    
    procedure TSimpleThread.WaitThreadStop;
    begin
      //等待线程结束
      StopThread;
      Terminate;
      SelfStart;
      WaitFor;
    end;
    
    end.
    
    uSimpleThread.pas
    

      

  • 相关阅读:
    GitHub Pages 绑定二级域名
    JS正则表达式(JavaScript regular expression)
    天猫魔盒远程安装APP
    'msbuild.exe' 不是内部或外部命令,也不是可运行的程序
    Jenkins自动更新与数据备份
    Jenkins插件无法更新、Jenkins插件不能下载问题解决
    安全测试工具wapiti的安装和使用(2)命令及参数解释
    安全测试工具wapiti的安装和使用(1)安装
    Jenkins远程构建和发布,基于IIS服务器(.netCore+vue)(三)
    Jmeter报错:java.net.ConnectException: Connection timed out: connect
  • 原文地址:https://www.cnblogs.com/approx/p/11852398.html
Copyright © 2020-2023  润新知