再谈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
喜欢的话,就在下面点个赞、收藏就好了,方便看下次的分享: