再谈delphi XE多线程同步对象及其管理二

再谈delphi XE多线程同步对象及其管理二

 

 

前言:不精通线程、不擅长对多线程进行管理,就不可能在当今多CPU多核心的年代写出优秀的程序代码,软件的性能将会大打折扣。本文及其示例代码,诠释System.Classes.pas中的(多)线程 和System.SyncObjs.pas (深入应用(多)线程时涉及的同步对象),System.Threading.pas线程、线程池、未来、任务及并行库的原理与应用。讲解了线程执行函数的最大响应时间(及设定的超时时间)、并发执行的线程数、CPU的核心数等,对线程池、最大工作线程、实际工作线程、空闲工作线程、CPU使用数、平均CPU使用数等性能指标的影响。还描述了线程在何时执行完毕,线程在何时进行释放,线程执行完毕是否等于线程得到释放,怎样通过代码控制线程的执行,怎样通过代码将线程彻底释放。期待对各位同学和同事有所帮助。

一、直接上代码

       代码执行的效果展示PC端:https://www.cpuofbs.com/app/samples/ThreadsAndCpu/ThreadsAndCpu.swf.html

       代码执行的效果展示手机端:https://www.cpuofbs.com/app/samples/ThreadsAndCpu/ThreadsAndCpu.flv

unit uMultiThreadSyncObjs;

interface

uses
  System.SysUtils,

  System.Types,
    //:任何一个应用系统自动加入的,其中含多线程同步等待及异步结果接口
  System.UITypes,
  System.Classes,
    //:任何一个应用系统自动加入的,其中含部分多线程类型的实现

  System.Variants,

  System.SyncObjs, //:需要用到多线程同步对象时
  System.Threading,

  FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
  FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects, FMX.ScrollBox, FMX.Memo,
  FMX.Edit, FMX.Layouts;

type
  TfmxMultiThreadAndAsynResult = class(TForm)
    btnIAsyncResultAndMultiWaitEventIm: TButton;
    Text_Tips: TText;
    RectText_Tips: TRectangle;
    MetropolisUIBlue_touch: TStyleBook;
    Memo1: TMemo;
    Button1: TButton;
    Layout1: TLayout;
    Label1: TLabel;
    Edit1: TEdit;

    ///<summary>用于并发测试的方法:</summary>
    procedure btnIAsyncResultAndMultiWaitEventImClick(Sender: TObject);
    ///<summary>用户退出窗体时,再次确认线程安全(因各线程内执行的任务太多、太快):</summary>
    procedure FormClose(Sender: TObject; var Action: TCloseAction);
    procedure Button1Click(Sender: TObject);
    procedure Edit1Change(Sender: TObject);

  private

    ///<summary>多线程的标识及管理数组</summary>
    LThreads :TArray<TThread>;  LmyThreadsID:TArray<Cardinal>;
    ///<summary>多线程的信号灯数组</summary>
    LEvents :TArray<TEvent>;
    procedure StopMe(Sender: TObject);


    { Private declarations }
  public
    { Public declarations }
  end;

var
  FRestTimeout:Integer =1000;
  //:20000: 超长时测试 1000: 长时测试 500: 中短时时测试 ;
  //:200: 短时测试 100: 短时测s试 10: 短时测试  5: 超短时测试
  LStopOrStart:ShortInt =1;
  LTest :Integer =0;
  LWorkerEvent:TProc;
  fmxMultiThreadAndAsynResult: TfmxMultiThreadAndAsynResult;

implementation
  uses
    System.Math,
    uMultiThreadSyncObjs_1;
{$R *.fmx}

procedure TfmxMultiThreadAndAsynResult.btnIAsyncResultAndMultiWaitEventImClick(
  Sender: TObject);
//:用于 多个单线程并发测试的方法:
var
  LThreadCounts:Cardinal;
begin
  if LEvents=nil then
  begin
    SetLength(LEvents,51);  SetLength(LThreads,51);
      //:初始化多线程数组及其信号灯数组
  end;
  if LStopOrStart=1 then
  begin
    btnIAsyncResultAndMultiWaitEventIm.Text:='停止';
    DEC(LStopOrStart);
  end else
  begin
    btnIAsyncResultAndMultiWaitEventIm.Text:='开始';
    INC(LStopOrStart);
  end;
  if not Assigned(LWorkerEvent) then
    LWorkerEvent:=
    procedure
      var LEventsCount: Cardinal;//:线程内的变量:
    begin //匿名线程的Excute代码:
      while LStopOrStart=0 do  //:让每个匿名线程无限的并发测试下去:
      begin
        TThread.Sleep(FRestTimeout); //:模拟一个后台或远程服务器的耗时操作
          //:如果是返回远程服务器的响应对象祖先类为TObject,响应对象如果下面需要与UI同步:
          //:则它们在被强行关闭时须用监视器加锁TMonitor.Enter(AObject);TMonitor.Exit(AObject);
        if LStopOrStart=1 then //:用户点停止,中断操作
        begin
          for LEventsCount := 1 to 50 do
          begin
            if LEvents[LEventsCount]<>nil then
            begin
              if LEvents[LThreadCounts].WaitFor( 0 )
                =TWaitResult.wrTimeout then
              begin
                LEvents[LEventsCount].SetEvent; //:点亮某线程的信号灯数组,停止该线程
                LEvents[LEventsCount].DisposeOf;//:信号灯数组中的该信号灯内存空间释放
              end;
            end;
          end;
          if LEvents<>nil then LEvents:=nil;  //:信号灯数组内存指针释放

          exit;  //:停止继续执行并跳出while循环调用者   //}
        end;
        if LStopOrStart=0 then //:用户点的是开始
        begin                  //ForceQueue  Synchronize 上锁,强制队列:
          System.SyncObjs.TInterlocked.Increment(LTest);
          //TThread.ForceQueue(TThread.Current, //nil,
          TThread.Synchronize(TThread.Current,//nil,
          procedure begin
            Text_Tips.BeginUpdate;
              Text_Tips.Text:=LTest.ToString;
            Text_Tips.EndUpdate;

            memo1.BeginUpdate;
              if memo1.Lines.Count>100 then memo1.Lines.Clear;
              //memo1.Lines.Add('当前CPU总数:'+TThread.ProcessorCount.ToString);
              //if not memo1.Lines.Text.Contains(LmyThreadsID[Cardinal(LThreadCounts)].ToString) then
              begin
                //memo1.Lines.Add('当前线程ID:'+LmyThreadsID[Cardinal(LThreadCounts)].ToString);
                memo1.Lines.Add('当前线程池最大工作线程数:'+TThreadPool.Current.MaxWorkerThreads.ToString);
                memo1.Lines.Add('当前工作线程数:'+TThreadPoolStats.Current.WorkerThreadCount.ToString);
                memo1.Lines.Add('当前空闲工作线程数:'+TThreadPoolStats.Current.IdleWorkerThreadCount.ToString);
                memo1.Lines.Add('当前CPU使用数:'+TThreadPoolStats.Current.CurrentCPUUsage.ToString);
                memo1.Lines.Add('当前平均CPU使用数:'+TThreadPoolStats.Current.AverageCPUUsage.ToString);
              end;
            memo1.EndUpdate;

            //if Edit1.Enabled<>false then Edit1.Enabled:=false;
          end );
          if LEvents[LThreadCounts]<>nil then
          begin
            if LEvents[LThreadCounts].WaitFor(0)=TWaitResult.wrSignaled then
            begin
              LEvents[LEventsCount].SetEvent; //:点亮某线程的信号灯数组,停止该线程
              LEvents[LEventsCount].DisposeOf;//:信号灯数组中的该信号灯内存空间释放
            end;
          end;
        end;
      end;

    end ;

  SetLength(LmyThreadsID,51);
  for LThreadCounts := 1 to 50 do
  begin //:开1200个单线程组成的多线程
    LmyThreadsID[Cardinal(LThreadCounts)]:=Cardinal(LThreadCounts);
    if LEvents[LThreadCounts]=nil then
      LEvents[LThreadCounts]:=TEvent.Create(false);
      //:为多线程中的每个线程设置其执行信号灯
    TThreadPool.Current.QueueWorkItem(LWorkerEvent);
    LThreads[LThreadCounts]:= //:在这样来识别和管理匿名多线程(线程数组)
    TThread.CreateAnonymousThread(
      //:创建一个独立的匿名线程线程,在其中完成UI界面交互的同步过程
      LWorkerEvent );
      //:匿名线程预定义,非立即执行方式:

    if LThreads[LThreadCounts]<>nil then
      LThreads[LThreadCounts].Start; //:开始执行匿名的多线程
  end;
end;

procedure TfmxMultiThreadAndAsynResult.StopMe(Sender: TObject);
var LThreadCounts:Cardinal;
begin
  if LEvents<>nil then //:如果标识多线程的信号灯数组内存指针尚未得以释放
  begin
    if LStopOrStart=0 then btnIAsyncResultAndMultiWaitEventImClick(Sender);
      //:如果处于运行状态就先点 停止,然后释放所有运行线程的内存:
    System.TMonitor.TryEnter(Text_Tips); Text_Tips.Locked:=true;
    System.TMonitor.TryEnter(btnIAsyncResultAndMultiWaitEventIm); btnIAsyncResultAndMultiWaitEventIm.Locked:=true;
    for LThreadCounts := 1 to 50 do
    begin
      if LEvents[LThreadCounts]<>nil then
      begin
        if LEvents[LThreadCounts].WaitFor( Cardinal(Ceil(FRestTimeout*0.025)) )
          =TWaitResult.wrTimeout then
        begin
          //:Waitfor多长时间:根据CPU硅晶片信号量,约线程:
            //:Excute或TNetHttpClient超时时间N的千分之23-26‰之间即可
            //:一般超时时间超过2000毫秒对用户就很不友好啦:可将其作为Timeout临界值Max
          //:取N* 25‰就好
          LEvents[LThreadCounts].SetEvent;   //:点亮某线程的信号灯数组,停止该线程
          LEvents[LThreadCounts].DisposeOf;  //:信号灯数组中的该信号灯内存空间释放
        end;
      end;
    end;
    LEvents:=nil;          //:信号灯数组释放内存指针(地址)
    LThreads:=nil;         //:多线程数组释放内存指针(地址)

    btnIAsyncResultAndMultiWaitEventIm.Locked:=false; System.TMonitor.Exit(btnIAsyncResultAndMultiWaitEventIm);
    Text_Tips.Locked:=false; System.TMonitor.Exit(Text_Tips);
  end;

end;

procedure TfmxMultiThreadAndAsynResult.FormClose(Sender: TObject;
  var Action: TCloseAction);
begin
  StopMe(Sender);
  close;
end;

procedure TfmxMultiThreadAndAsynResult.Edit1Change(Sender: TObject);
var Value: Integer;
begin
  if Edit1.Text.Trim='' then Edit1.Text:='1000';
  if TryStrToInt( Edit1.Text.Trim, Value)=true then
  begin
    FRestTimeout:=Value;
    memo1.Lines.Add('当前CPU总数:'+TThread.ProcessorCount.ToString);
  end;
end;

procedure TfmxMultiThreadAndAsynResult.Button1Click(Sender: TObject);
begin
  if fmxMultiThreadAndAsynResult_1=nil then
    Application.CreateForm(TfmxMultiThreadAndAsynResult_1, fmxMultiThreadAndAsynResult_1);
  fmxMultiThreadAndAsynResult_1.Show;
end;

end.

二、原理参考

(一)、官方

1、System.Classes.pas中的(多)线程  和System.SyncObjs.pas (深入应用(多)线程时涉及的同步对象)

1.1、TThread 线程 http://docwiki.embarcadero.com/Libraries/Rio/en/System.Classes.TThread

1.2、TThread.CreateAnonymousThread 匿名线程 :

http://docwiki.embarcadero.com/Libraries/Rio/en/System.Classes.TThread.CreateAnonymousThread

1.3、System.SyncObjs.TEvent (多)线程信号灯同步对象

1.4、TThread.Synchronize(多)线程同步过程、TThread.Queue(多)线程队列(异步)过程:

http://docwiki.embarcadero.com/Libraries/Rio/en/System.Classes.TThread.Synchronize

http://docwiki.embarcadero.com/Libraries/Rio/en/System.Classes.TThread.Queue

1.5、其它的线程安全同步对象:详见System.SyncObjs.pas

1.6、底层的http://docwiki.embarcadero.com/Libraries/Rio/en/System.SyncObjs.TCriticalSection线程安全临界区同步对象

1.7、底层的System.SysUtils.TMultiReadExclusiveWriteSynchronizer (原子)多读少写(独占写)同步器

2、System.Threading.pas

http://docwiki.embarcadero.com/Libraries/Rio/en/System.Threading

2.1、TThreadPool 线程池  

2.2、IFuture  未来

2.3、ITask 任务

2.4、TParallel 并行库

(二)、本博客经验谈

1、delphiXE关于线程和多线程、线程的同步与异步执行 https://blog.csdn.net/pulledup/article/details/105617706

2、大家都要好好学习delphi XE线程同步对象单元System.SyncObjs https://blog.csdn.net/pulledup/article/details/106136991

3、delphi XE多线程同步对象及其管理 https://blog.csdn.net/pulledup/article/details/106150698

4、源码示例2:delphiXE多线程同步对象及异步执行.zip https://download.csdn.net/download/pulledup/12424951 本下载解决delphiXE在处理多线程时,如何对各线程进行管理、如何做到中途中断执行多线程时的线程安全,如何在多线程内部进行UI同步等等,还可看我的上面这个第3、博文同步。

5、本案源码:https://download.csdn.net/download/pulledup/12506035

喜欢的话,就在下面点个赞、收藏就好了,方便看下次的分享:

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

专讲冷知识

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值