对于实现线程化电子邮件发送队列的 class,设计会是什么样子?
What would the design look like for a class that implements a threaded email sending queue?
所以我的应用程序是一个出租车呼叫调度系统,其中接线员通过 phone 接收呼叫并将接收到的呼叫分配给 driver。在输入呼叫详细信息的呼叫详细信息表单上,单击保存按钮后,表单应立即关闭,应在后台线程中向出租车 driver 发送电子邮件,并显示主表单,他可以 select 下一次调用并将其分配给另一个 driver。
设计的难点在于它应该是一个队列,这意味着队列中的电子邮件需要等到正在发送的电子邮件处理完成。当然,电子邮件发送发生在与主 VCL 线程不同的线程中。
通过从主线程到电子邮件线程的函数调用,只有返回到主线程的响应是队列中剩余要发送的电子邮件数量。例如。如果用户试图关闭应用程序,它会询问电子邮件队列 class 队列中是否有待处理的电子邮件。
在 Delphi 中,哪种 class 设计和构造最能实现此功能?我确信它有一个或多个 TThreads,但除此之外我就迷路了。我本可以开始写一些东西,但我确定它会是错误的,我需要把它拆开,post 在这里并根据建议重新开始。这就是为什么我先 post 在这里编辑 - 我想要设计技巧。由于我使用的是 Delphi,因此我在此处 post 编辑,因为我需要 Delphi-specific 设计技巧。预先感谢任何答案。
注意:我愿意使用 TThread 实现或 OmniThreadLibrary。
我为 class 添加了一个基本模板,答案将需要对其进行扩展和构建。
unit uEmailQueue;
interface
uses Classes;
type
TEmailServer = record
SMTPHost: String;
SMTPPort: Integer;
SMTPUseSSL: Boolean;
SMTPUserName: String;
SMTPPassword: String;
SMTPSenderName: String;
end;
TEmailMessage = record
RecipientEmailAddr: String;
EmailSubject: String;
EmailMessage: String;
end;
TEmailQueue = class(TObject)
private
FEmailServer: TEmailServer;
public
procedure SendEmail(AEmailMessage: TEmailMessage);
end;
implementation
{ TEmailQueue }
procedure TEmailQueue.SendEmail(AEmailMessage: TEmailMessage);
begin
end;
end.
我建议你设置OTL。
您很可能拥有唯一的 SMTP 服务器,因此只有一个邮件发送线程,以防您稍后添加更多通知发送服务器(例如向不同的 phone 运营商发送 SMS)- 您将添加更多传出 "sink" 线程。
我个人会考虑使用管道模式。
- http://www.thedelphigeek.com/2010/11/multistage-processes-with.html
- http://otl.17slon.com/book/doku.php?id=book:highlevel:pipeline
目前它看起来主要是过度设计,但它稍后会为您提供定制和扩展(比如添加比发送电子邮件更多的通知方式,比如记录过程,比如添加 "VIP client/regular client/shady client/fraud"启发式)或其他任何东西。
由于管道以一些线程安全的源数据开始collection/container,要开始处理新请求,您只需将任务记录放入该队列。
- http://otl.17slon.com/book/doku.php?id=book:highlevel:blockingcollection
- http://otl.17slon.com/book/doku.php?id=book:introotl:tomnivalueintro
类似于
var TaxiInQueue: iOmniBlockingCollection;
....
var NewTask: TEmailMessage;
...
TaxiInQueue.Add( TOmniValue.FromRecord(NewTask) );
同样,这将使您可以从单线程 GUI 应用程序(使用单个代理输入新任务)扩展到网络代理(3 层或 WWW-servlet),使用多个线程同时输入新任务不同 connections/threads.
There only response back to the main thread would be the number of emails remaining to be sent in the queue
就我个人而言,我会以 "double accounting" 的方式进行,我会保留两个变量:创建的任务总数和完成的任务总数。它们可以由主线程中单独的不可见 window 管理(AllocateHWND 并使用 PostMessage 触发增量),或单独的工作线程(可能过度设计),或只是一些具有任何类型 MREW 锁的对象在里面。
增量将是当前(或稍微过时的)队列长度。此外,windows/thread/object 将能够生成自己的事件(比如在队列空闲、正常或溢出时绘制一些仪表)
关于MREW锁,Delphi RTL中有一个,OTL库本身有一个,还有人将OmniMREW移植到Lzarus,然后又做了一个他声称的锁效率更高
所以我的应用程序是一个出租车呼叫调度系统,其中接线员通过 phone 接收呼叫并将接收到的呼叫分配给 driver。在输入呼叫详细信息的呼叫详细信息表单上,单击保存按钮后,表单应立即关闭,应在后台线程中向出租车 driver 发送电子邮件,并显示主表单,他可以 select 下一次调用并将其分配给另一个 driver。
设计的难点在于它应该是一个队列,这意味着队列中的电子邮件需要等到正在发送的电子邮件处理完成。当然,电子邮件发送发生在与主 VCL 线程不同的线程中。
通过从主线程到电子邮件线程的函数调用,只有返回到主线程的响应是队列中剩余要发送的电子邮件数量。例如。如果用户试图关闭应用程序,它会询问电子邮件队列 class 队列中是否有待处理的电子邮件。
在 Delphi 中,哪种 class 设计和构造最能实现此功能?我确信它有一个或多个 TThreads,但除此之外我就迷路了。我本可以开始写一些东西,但我确定它会是错误的,我需要把它拆开,post 在这里并根据建议重新开始。这就是为什么我先 post 在这里编辑 - 我想要设计技巧。由于我使用的是 Delphi,因此我在此处 post 编辑,因为我需要 Delphi-specific 设计技巧。预先感谢任何答案。
注意:我愿意使用 TThread 实现或 OmniThreadLibrary。
我为 class 添加了一个基本模板,答案将需要对其进行扩展和构建。
unit uEmailQueue;
interface
uses Classes;
type
TEmailServer = record
SMTPHost: String;
SMTPPort: Integer;
SMTPUseSSL: Boolean;
SMTPUserName: String;
SMTPPassword: String;
SMTPSenderName: String;
end;
TEmailMessage = record
RecipientEmailAddr: String;
EmailSubject: String;
EmailMessage: String;
end;
TEmailQueue = class(TObject)
private
FEmailServer: TEmailServer;
public
procedure SendEmail(AEmailMessage: TEmailMessage);
end;
implementation
{ TEmailQueue }
procedure TEmailQueue.SendEmail(AEmailMessage: TEmailMessage);
begin
end;
end.
我建议你设置OTL。
您很可能拥有唯一的 SMTP 服务器,因此只有一个邮件发送线程,以防您稍后添加更多通知发送服务器(例如向不同的 phone 运营商发送 SMS)- 您将添加更多传出 "sink" 线程。
我个人会考虑使用管道模式。
- http://www.thedelphigeek.com/2010/11/multistage-processes-with.html
- http://otl.17slon.com/book/doku.php?id=book:highlevel:pipeline
目前它看起来主要是过度设计,但它稍后会为您提供定制和扩展(比如添加比发送电子邮件更多的通知方式,比如记录过程,比如添加 "VIP client/regular client/shady client/fraud"启发式)或其他任何东西。
由于管道以一些线程安全的源数据开始collection/container,要开始处理新请求,您只需将任务记录放入该队列。
- http://otl.17slon.com/book/doku.php?id=book:highlevel:blockingcollection
- http://otl.17slon.com/book/doku.php?id=book:introotl:tomnivalueintro
类似于
var TaxiInQueue: iOmniBlockingCollection;
....
var NewTask: TEmailMessage;
...
TaxiInQueue.Add( TOmniValue.FromRecord(NewTask) );
同样,这将使您可以从单线程 GUI 应用程序(使用单个代理输入新任务)扩展到网络代理(3 层或 WWW-servlet),使用多个线程同时输入新任务不同 connections/threads.
There only response back to the main thread would be the number of emails remaining to be sent in the queue
就我个人而言,我会以 "double accounting" 的方式进行,我会保留两个变量:创建的任务总数和完成的任务总数。它们可以由主线程中单独的不可见 window 管理(AllocateHWND 并使用 PostMessage 触发增量),或单独的工作线程(可能过度设计),或只是一些具有任何类型 MREW 锁的对象在里面。
增量将是当前(或稍微过时的)队列长度。此外,windows/thread/object 将能够生成自己的事件(比如在队列空闲、正常或溢出时绘制一些仪表)
关于MREW锁,Delphi RTL中有一个,OTL库本身有一个,还有人将OmniMREW移植到Lzarus,然后又做了一个他声称的锁效率更高