PPL并行编程库02-多任务

PPL并行编程库02-多任务

一、何为多任务

        多任务,即上一个任务尚未执行完毕,下一个任务又启动啦,操作系统中,同时又N个任务在运行,即操作系统的多任务。MSWIDOWS是最早支持多任务的操作系统,POSIX也支持。

二、多任务的并行

2.1、场景

(1)、后台任务按钮:连续点击

(2)、任务中,以TParallel.Join( TArray<TProc> ) 并行加入的方式,同时执行着多个过程函数。

2.1、代码示例:并行的多任务

并行的多任务: 分为,多任务异步执行,和多任务同步执行,两种方式,在下一讲进行讨论。此案例,我们采用的是异步多任务:

procedure TfmxDictionary.getDatabaseFromServer(
  DatatableNameOfGetData:string;TotalRecCountOfGetData :Integer;
  BeginRecNoOfGetData,RecCountPerPageOfGetData:Integer;
  pSql,pSelectKey:string);

var ATask:ITask; 
    FDJsonDatasets:TFDJsonDatasets;
    FDAdaptedDataSet:TFDAdaptedDataSet;
begin

    // btnListViewMain_Query.Enabled:=false;  // : 见三、共享资源锁的进入和退出的3.2.2、第二种方式:利用UI按钮等的Enabled或Visible的状态进行控制

  pCurrtPage1:='1';  pRecsPerPage1:='20';
  pKeyMaster:='取物品资料表';
  pSqlMaster:=' select top 600 com_id,item_id,item_name,item_type, '
    +' item_spec,maintenance_datetime from ctl03001 where com_id=''001'' ';
  pKeyDetail:=''; pSqlDetail:='';
  pKeyDetaildetail:=''; pSqlDetaildetail:='';

  if TMonitor.TryEnter(FDMT_SQLiteMain)=true then
    TMonitor.Wait(FDMT_SQLiteMain,0);   //: 询问等待脉冲:Pulse
若等到了:说明下面的代码已经把它解锁啦

  TMonitor.Enter(FDMT_SQLiteMain,0);  // :共享资源加锁

  Async.Run<Boolean>(
   
function :Boolean
    begin //:1.在后台线程中执行并未2.和3.回调成功或异常Result结果:
      try
        //:注意窗体产生时的:FDMT_SQLiteMain.FetchOptions
        FDJsonDatasets:=ClientModule1.ServerMethods1Client
          .getMatserDetailFDJson(
            pCurrtPage,pRecsPerPage,
            pKeyMaster,pSqlMaster,
            pKeyDetail,pSqlDetail,pKeyDetaildetail,pSqlDetaildetail
          );
        FDAdaptedDataSet := TFDJSONDataSetsReader.GetListValueByName(
          FDJsonDatasets ,'取物品资料表');
        //if FDMT_SQLiteMain.Active=true then FDMT_SQLiteMain.Active :=False;
        //while FDMT_SQLiteMain.State<>dsInactive do sleep(0);
          //:如果内存表在原有基础上追加数据:
            //:就不要在其之前关闭内存表,否则就是在更新数据
          //:如果连续滑动ListView或执行本点击:
            //:任务是后台多线程,找CPUs的空闲,1个任务的线程把它关了,
              //:另1个任务的线程又把它打开了
            //:关闭内存表:就会有冲突:会提示你:
              //:UnEnable this Operation in a Closed Dataset

        FDMT_SQLiteMain.AppendData( FDAdaptedDataSet );  //:已含字段定义//FDMT_SQLiteMain.FieldDefs:=FDAdaptedDataSet.FieldDefs;
          //:FDAdaptedDataSet需要Rest分页取不同的数据
            //:追加给内存表: 关闭按钮的状态来控制

        // : FDMT_SQLiteMain:供数据的删改查用
        FDMemTemp.CloneCursor(FDAdaptedDataSet);  //: FDMemTemp:供UI追加后台数据的加载使用:

            //:注意窗体产生时的:FDMemTemp.FetchOptions
        if FDMT_SQLiteMain.Active=False then FDMT_SQLiteMain.Open;
        while FDMT_SQLiteMain.State=dsInactive do sleep(0);

        FDMT_SQLiteMain.IndexFieldNames:='item_id';//'com_id;item_id';
        FDMT_SQLiteMain.IndexesActive:=true;//:会触发按索引排序

        if FDMT_SQLiteMain.Active=true then FDMT_SQLiteMain.First;
        //测试://FDAdaptedDataSet.SaveToFile(

             //System.IOUtils.TPath.GetTempPath+'FDAdaptedDataSet.json',TFDStorageFormat.sfJSON  );
      finally

        if (FDMT_SQLiteMain.Active=true) then
          Result:=true       //:'成功执行任务'
        else Result:=false;  //:'未成功执行任务'

      end;
    end,
   
procedure(const Success:Boolean)
    begin
 //:2.在UI线程中执行(如果1.返回正确的数值)
      if Success=true then
      begin
        if FDMT_SQLiteMain.Active=true
          //and (FDMT_SQLiteMain.ChangeCount>0)
        then
        begin
          try
            if (FDMT_SQLiteMain.ChangeCount>0) then
              UpdateMyListViewItem(ListViewMain); 
//: 与UI线程对话
          finally
            Memo1.Lines.Clear; Memo1.Lines.Add('任务执行完毕,字段1:'
              +FDMT_SQLiteMain.FieldList[1].FieldName+'记录数:'+IntToStr(FDMT_SQLiteMain.ChangeCount));
            if (FDMT_SQLiteMain.Active=true)
              and (ListViewMain.Items.Count>0)
            then ListViewMain.ScrollTo(0);
           
// btnListViewMain_Query.Enabled:=true;  // : 见三、共享资源锁的进入和退出的3.2.2、第二种方式:利用UI按钮等的Enabled或Visible的状态进行控制

            TMonitor.Exit(FDMT_SQLiteMain);  // :共享资源解锁
          end;
        end;
      end;
    end,

    procedure(const Ex:Exception)
    begin  //:3.处理1.中的异常
      if Ex.ClassType=EMonitorLockException then
        ShowMessage('别点得那么猛:' + sLineBreak + '还在加载上一组数据!')  // :未锁定共享资源的异常:上面解决掉,不可能出现
      else ShowMessage('出错啦:' + sLineBreak + Ex.Message);  // :获取数据异常
     
// btnListViewMain_Query.Enabled:=true;  // : 见三、共享资源锁的进入和退出的3.2.2、第二种方式:利用UI按钮等的Enabled或Visible的状态进行控制


      TMonitor.Exit(FDMT_SQLiteMain);  // :共享资源解锁
    end
  );

end ;

unit AsyncTask;

interface

uses
  System.SysUtils, System.Threading;

type
  TAsyncBackgroundTask<T> = reference to function: T;
  TAsyncSuccessCallback<T> = reference to procedure(
    const TaskResult:T );
  TAsyncErrorCallback = reference to procedure(
    const E: Exception );
  TAsyncDefaultErrorCallback = reference to procedure(
    const E: Exception;
    const ExptAddress: Pointer );

  Async = class sealed
  public
    class function Run<T>(
      Task: TAsyncBackgroundTask<T>;
      Success: TAsyncSuccessCallback<T>;
      Error: TAsyncErrorCallback = nil ):ITask;
  end;

var
  DefaultTaskErrorHandler: TAsyncDefaultErrorCallback = nil;

implementation

uses
  System.Classes;

{ Async }

class function Async.Run<T>(
  Task: TAsyncBackgroundTask<T>;
  Success: TAsyncSuccessCallback<T>;
  Error: TAsyncErrorCallback ) :ITask;
var
  LRes: T;
begin
  Result := TTask.Run(
    procedure
    var
      Ex: Pointer;
      ExceptionAddress: Pointer;
    begin
      Ex := nil;
      try
        LRes := Task(); //:1.后台任务的方法:返回泛型T
        if Assigned(Success) then
          //:如果指定了参数Success的类型及其参照reference
        begin
          TThread.Queue(nil, //:Queue在主线程(UI线程)中运行
            procedure
            begin
              Success(LRes); //:2.队列化-后台任务的成功回调的函数:传入这个泛型T
            end);
        end;
      except
        Ex := AcquireExceptionObject;
          //:请求系统返回异常对象:system.AcquireExceptionObject
        ExceptionAddress := ExceptAddr;
          //:请求系统返回异常地址:system.ExceptAddr
        TThread.Queue(nil, //:Queue在主线程(UI线程)中运行
          procedure
          var
            LCurrException: Exception;
              //:系统异常类Exception实例变量
          begin
            LCurrException := Exception(Ex);
              //:系统异常类获取某个异常对象
            try
              if Assigned(Error) then
                Error(LCurrException)
                //:3.1.队列化-后台任务的异常回调的函数:传入这个异常LCurrException
              else
                DefaultTaskErrorHandler(
                  LCurrException,
                  ExceptionAddress );
                  //:3.2.队列化-后台任务的默认异常回调的函数:传入这个异常LCurrException及其地址ExceptionAddress
            finally
              FreeAndNil(LCurrException);
            end;
          end);
      end;
    end);
end;

initialization

DefaultTaskErrorHandler :=
  procedure(
    const E: Exception;
    const ExceptionAddress: Pointer )
  begin
    ShowException(E, ExceptionAddress);
  end;

end.

三、多任务并行或线程执行的注意事项

3.1、共享资源

        多个任务在并行,或者线程在执行。此时,有可能别的任务或线程也同时正在操作某个资源,这个资源,被看成是共享资源。比如:内存表,比如从内存表加载数据的TListView或TListBox等等。

        此时需要任务或线程对共享资源进行加锁运行后解锁,任务或线程在线程池中,以队列的方式按先进先出(FIFO : First-In-First-Out)原则 ( 当然任务是多线程的,其线程池会主动找CPUs或GPUs的idle空闲,优先执行)使用这些共享资源。

3.2、共享资源锁的进入和退出

3.2.1、第一种方式:运用锁的脉冲进行控制

     // ......任务开始前(最佳的处理方式:连续处理方式:完全不阻塞的方式):

        if TMonitor.TryEnter ( LObject1 ) then // : 如果LObject1要再次进入锁定

            TMonitor.WaitFor ( LObject1,0 ) ;  // : 等待LObject1(解锁)上一次的脉冲信号(脉冲:有锁的任何变化)

     // ......开始任务代码

        TMonitor.Enter ( LObject1 ,0 ) ; //: 锁定1

            TMonitor.Enter ( LObject2 ,0 ) ; //: 锁定2

             // ......你对共享资源的处理

             // 共享资源可以嵌套:后进先出 : LIFO (Last-In-First-Out)  ,不采用先进先出FIFO (First-In-First-Out)

            TMonitor.Exit ( LObject2 ) ; //: 解锁2

        TMonitor.Exit ( LObject1 ) ; //: 解锁1

     // ......任务代码结束

3.2.2、第二种方式:利用UI按钮等的Enabled或Visible的状态进行控制

     // ......任务开始前(非连续处理方式:不阻塞UI,但UI按钮的状态会等待上次任务的结束后,方可再次点击执行下一次任务):

     btnListViewMain_Query.Enabled:=false;

     // ......开始任务代码

        TMonitor.Enter ( LObject1 ,0 ) ; //: 锁定1

            TMonitor.Enter ( LObject2 ,0 ) ; //: 锁定2

             // ......你对共享资源的处理

             // 共享资源可以嵌套:后进先出 : LIFO  ,不采用先进先出PIFO

            TMonitor.Exit ( LObject2 ) ; //: 解锁2

        TMonitor.Exit ( LObject1 ) ; //: 解锁1

     btnListViewMain_Query.Enabled:=true;

     // ......任务代码结束

发布了61 篇原创文章 · 获赞 6 · 访问量 5581

猜你喜欢

转载自blog.csdn.net/pulledup/article/details/102068352