文档处理各阶段并发执行设计模式的实现

来源:互联网 发布:mac强制退出应用程序 编辑:程序博客网 时间:2024/04/27 20:01

    Delphi应用程序实际开发应用中,经常会遇到文档或数据的处理流水线,比如,要打印输出一系列文档,一般的做法,都是在主线程中一条线完成的,如下图所示:

   

这种方式的执行效率比较低,因为创建文档或打印文档阶段,可能都是比较耗时的,用户体验就是整个过程耗时比较长。实际上,整个流水线是可以分割为多个阶段,以多线程的方式进行并发处理的,如下图所示:

 

其中,创建文档和打印文档分别由一个线程来执行,这样可以避免各个阶段的相互等待,提高整个流程的执行效率。

其实,整个模式的实现并没有多少技术含量,主要是演示了线程间如何相互协调工作的一种实现方式,算是一项记录。我想特别表达的,就是要放开思路的感触,设计思路的转变,可以带来更好的应用效果。Delphi VCL应用程序的开发,也可以随处使用多线程技术,也可以很精彩。同时,通过此模式的实现及应用,也提高了自己对于多线程开发、Delphi语言一些新元素的认知。

 

下面就列出整个实现的框架代码,希望大家指正。

-------------------------------
unit TaskBlockUnit;

interface

type
  // TTaskblock类,包含了进行文档打印时所需要的各种字段或信息。

  // 这里只是个空类,不用展开了。

  TTaskblock = class
  private
    FBlockID: string;
    procedure DoCreateDoc(const Value: string);
    procedure SetBlockID(const Value: string);
  protected
    { protected declarations }
  public
    constructor Create(const ID: string);
  public
    property BlockID: string read FBlockID write SetBlockID;
  end;

implementation

{ TTaskblock }

constructor TTaskblock.Create(const ID: string);
begin
  FBlockID := ID;
  DoCreateDoc(ID);
end;

procedure TTaskblock.SetBlockID(const Value: string);
begin
  FBlockID := Value;
end;

procedure TTaskblock.DoCreateDoc(const Value: string);
begin
    // 完成文档的创建
end;

end.

--------------------------------

unit TskPrtMgt;

interface

uses
  Classes, Generics.Collections, SyncObjs,
  TaskBlockUnit;

const
  WAIT_TIMEOUT = 1000;

type
  TTaskblockBaseThread = class(TThread)
  protected
    // OutputMsg为线程定义一个向外部报告情况的机制。
    FMsgStr: string;
    procedure OutputMsg;
  public
    constructor Create;
  end;

  TCreateTaskblockThread = class(TTaskblockBaseThread)
  private
    //  CreateNewTaskblock完成创建任务对象的工作
    // 打印线程利用此对象中的信息,进行打印输出
    function CreateNewTaskblock(TaskblockId: string): TTaskblock;
  protected
    procedure Execute; override;
  end;

  TPrintTaskblockThread = class(TTaskblockBaseThread)
  private
    // PrintOut根据传入对象的信息,执行打印输出操作。
    procedure PrintOut(const Instance: TTaskblock);
  protected
    procedure Execute; override;
  end;

  TTaskblockPrintManager = class
  private
    // 定义一个类变量,保存唯一一个打印管理器的实例,实现单例模式
    class var
      FManager: TTaskblockPrintManager;
  private
    // 序列队列
    FTskIDQueue: TQueue<String>;
    // 打印队列
    FTskQueue: TObjectQueue<TTaskblock>;
    // 两个队列状态事件类
    FPrtQueueNotEmpty,
    FTskIdQueueNotEmpty: TEvent;
    // 外部传入的消息列表,比如TMemo.Lines对象
    // 管理器用来向外部发布消息。
    FMsgStrs: TStrings;
    //创建任务线程
    FCrtTskThread: TCreateTaskblockThread;
    // 打印任务线程
    FPrtTskThread: TPrintTaskblockThread;
  protected
    constructor Create;
  public
    destructor Destroy; override;
    // 外部通过AddTaskblockID向管理器输入任务标识符。
    procedure AddTaskblockID(const ID: string);
  public
    property MsgStr: TStrings read FMsgStrs write FMsgStrs;
  end;

// 任务管理器的Create函数是对外隐藏的,不能直接创建实例,只能
// 通过此TaskblockPrintManager函数返回唯一一个管理器实例
function TaskblockPrintManager: TTaskblockPrintManager;

implementation

function TaskblockPrintManager: TTaskblockPrintManager;
begin
  if TTaskblockPrintManager.FManager = nil then
    Result := TTaskblockPrintManager.Create
  else Result := TTaskblockPrintManager.FManager;
end;

{ TTaskblockBaseThread }

constructor TTaskblockBaseThread.Create;
begin
  // 统一定义线程创建后立即执行的行为
  inherited Create(False);
end;

procedure TTaskblockBaseThread.OutputMsg;
begin
  if TaskblockPrintManager.MsgStr <> nil then
  begin
    TaskblockPrintManager.MsgStr.Add(FMsgStr);
  end;
end;

{ TCreateTaskblockThread }

function TCreateTaskblockThread.CreateNewTaskblock(TaskblockId: string): TTaskblock;
begin
  Result := TTaskblock.Create(TaskblockId);
end;

procedure TCreateTaskblockThread.Execute;
var
  CurID: string;
  TskObj: TTaskblock;
  wf: TWaitResult;
begin
  while not Terminated do
  begin
    wf := TaskblockPrintManager.FTskIdQueueNotEmpty.WaitFor(WAIT_TIMEOUT);
    if wf = wrTimeout then
    begin
      if Terminated then Exit;
      Continue;
    end else if wf <> wrSignaled then Exit;

    // 共享资源被访问时,加锁是必要的。
    System.TMonitor.Enter(TaskblockPrintManager.FTskIDQueue);
    try
      CurID := TaskblockPrintManager.FTskIDQueue.Dequeue;
      // CurID=''表示队列空了,需要重置事件,让当前线程重新进入等待状态
      if CurID = '' then       
        TaskblockPrintManager.FTskIdQueueNotEmpty.ResetEvent;
    finally
      System.TMonitor.Exit(TaskblockPrintManager.FTskIDQueue);
    end;

    if CurID = '' then TskObj := nil
    else TskObj := CreateNewTaskblock(CurID);
    System.TMonitor.Enter(TaskblockPrintManager.FTskQueue);
    try
      // 向打印队列传入已创建的对象,同时通知打印线程有工作要做了。
      TaskblockPrintManager.FTskQueue.Enqueue(TskObj);
      TaskblockPrintManager.FPrtQueueNotEmpty.SetEvent;
    finally
      System.TMonitor.Exit(TaskblockPrintManager.FTskQueue);
    end;
  end;
end;

{ TPrintTaskblockThread }

procedure TPrintTaskblockThread.Execute;
var
  Instance: TTaskblock;
  wf: TWaitResult;
begin
  while not Terminated do
  begin
    wf := TaskblockPrintManager.FPrtQueueNotEmpty.WaitFor(WAIT_TIMEOUT);
    if wf = wrTimeout then
    begin
      if Terminated then Exit;
      Continue;
    end else if wf <> wrSignaled then Exit;

    System.TMonitor.Enter(TaskblockPrintManager.FTskQueue);
    try
      Instance := TaskblockPrintManager.FTskQueue.Extract;
      // Instance=nil,表示打印队列空了,重置事件,当前线程进入等待状态
      if Instance = nil then TaskblockPrintManager.FPrtQueueNotEmpty.ResetEvent
    finally
      System.TMonitor.Exit(TaskblockPrintManager.FTskQueue);
    end;

    if Instance <> nil then
    begin
      try
        PrintOut(Instance);
      finally
        Instance.Free;
      end;
    end;
  end;
end;

procedure TPrintTaskblockThread.PrintOut(const Instance: TTaskblock);
begin
  FMsgStr := Instance.BlockID;
  //向外部发布消息,一般都要操作GUI,所以需要同步执行。
  Synchronize(OutputMsg);
end;

{ TTaskblockPrintManager }

procedure TTaskblockPrintManager.AddTaskblockID(const ID: string);
begin
  System.TMonitor.Enter(FTskIDQueue);
  try
    FTskIDQueue.Enqueue(ID);
  finally
    System.TMonitor.Exit(FTskIDQueue);
  end;
  // 通知创建任务线程,有工作要做了。
  FTskIdQueueNotEmpty.SetEvent;
end;

constructor TTaskblockPrintManager.Create;
begin
  FPrtQueueNotEmpty := TEvent.Create();
  FTskIdQueueNotEmpty := TEvent.Create();
  FTskQueue := TObjectQueue<TTaskblock>.Create(False);
  FTskIDQueue := TQueue<string>.Create;

  // FManager赋值语句,放在创建两个线程前面很重要
  FManager := Self;
  FCrtTskThread := TCreateTaskblockThread.Create;
  FPrtTskThread := TPrintTaskblockThread.Create;
 end;

destructor TTaskblockPrintManager.Destroy;
begin

  // 释放两个事件类,还有中止相关线程执行的效果

  FPrtQueueNotEmpty.Free;
  FTskIdQueueNotEmpty.Free;
  FCrtTskThread.Free;
  FPrtTskThread.Free;
  FTskQueue.OwnsObjects := True;
  FTskQueue.Free;
  FTskIDQueue.Free;
  FManager := nil;

end;

Initialization
  TTaskblockPrintManager.Create;

finalization
  if TTaskblockPrintManager.FManager <> nil then
    TTaskblockPrintManager.FManager.Free;

end. 

原创粉丝点击