您的位置:首页 > 其它

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

2011-07-16 13:45 363 查看
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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: