原需求:
B/S结构的系统里,用户点一个按钮系统开始发送上千封邮件,要求把发送信息(发送成功数,失败数,剩余数量...)动态实时的反馈给客户.
原文的技术误用之处:
(1)ajax,pageload 这些ui层的东东渗透到逻辑层里去了
(2)thread的职责太多
并不是网页上的多线程不好搞,而是Web开发搞久了,基本的OO设计能力下降了,或者根本就忽略OO设计了,眼中只有页面。
以原文这个例子而言,你不把它当成Web开发,一切就变得很简单了。
我的解决方案:
首先,得有一个类管理每条信息。

public abstract class MessageHolder
{
public DateTime SendTime { get; set; }
public String ReceiverMail { get { return Receiver.Mail; } }
public String ErrorMessage { get; set; }
public Int32 SendCount { get; set; }
public Boolean SendExpired { get { return SendCount > 5; } }
public Boolean SendOk { get; private set; }
public Guid Id { get; private set; }
public String SendResult {
get {
if (SendOk) return "发送成功";
else
{
if (SendExpired) return "发送失败";
else return "等待发送";
}
}
}
}
public class MailMessageHolder : MessageHolder
public String Title { get; private set; }
public String Text { get; private set; }

然后,得有一个容器管理用户的发送信息

public class MessageCache<TMessageHolder> where TMessageHolder : MessageHolder
protected SortedDictionary<String, IList<TMessageHolder>> Cache { get; set; } // Cache(ReceiverMail, MsgHolders),我的程序主要功能不是群发邮件,所以这样设计
public MessageCache()
{
Cache = new SortedDictionary<string, IList<TMessageHolder>>();
public void Add(TMessageHolder msgHolder)
public IList<TMessageHolder> GetAllMessage()
public IList<TMessageHolder> GetAllMessageByReceiver(String receiverMail)
public IList<TMessageHolder> FindNeedToSendMessageHolders(IList<TMessageHolder> holderList)
if (holderList == null) return null;
IList<TMessageHolder> hl = new List<TMessageHolder>();
foreach (TMessageHolder h in holderList)
{
if ((!h.SendExpired) && (!h.SendOk)) hl.Add(h);
return hl;
public IList<TMessageHolder> FindSendOkMessageHolders(IList<TMessageHolder> holderList)

最后,得有线程发送

public void ThreadStart_SendMailMessageCache()
while (true)
if (ExitAllDaemonThreads) return;
IList<MailMessageHolder> hl = this.MailMessageCache.FindNeedToSendMessageHolders(MailMessageCache.GetAllMessage());
if (hl == null || hl.Count == 0)
lock (SendMailMessageCacheThreadSyncRoot)
{
Monitor.Wait(SendMailMessageCacheThreadSyncRoot);
continue;
}
foreach (MailMessageHolder h in hl)
if (ExitAllDaemonThreads) return;
if (h.SendExpired) continue;
if (!h.SendOk)
{
try
{
SendMail(h);
Thread.Sleep(1000);
}
catch (SmtpException se)
h.ErrorMessage = se.StatusCode.ToString();
}
Thread.Sleep(30000);

当有新发送任务时先将任务批量添加到MessageCache,然后Monitor.PulseAll(SendMailMessageCacheThreadSyncRoot)一下。如果觉得后台线程太多不好的话,也可以没有任务时中止线程,有任务时再启动。
这种解决方案的好处非常明显:
(1)无论是B/S程序还是C/S程序还是控制台程序都使用,换个UI非常简单
(2)查看发送进度:
MessageCache<TMessageHolder>.GetAllMessage(),搞个GridView显示下就行了,可以看具体的发送信息。也可以计算统计信息,都挺简单的。
(3)具备发送失败自动重发功能
(4)用户可以同时提交多个发送任务
(5)用个持久化方案就可以实现断点重发。B/S用数据库,C/S用文件或数据库
(6)与Session无关
(7)后台非常柔性。你可以写一个线程处理多个MessageCache,也可以多个线程处理多个MessageCache,也可以多个线程处理一个MessageCache,也可以采用异步方式处理,也可以多线程+异步,也可以用协程调度,也可以用event-dispatch-scheduler方式处理,也可以存在数据库让几台机器一起发,也可以像TCP协议那样加入拥塞控制机制。总之根据需求与服务器的负载情况来。
这种解决方案适用面很广,比如,我写的B/S式的Spider,可以多个用户同时登陆使用,每个用户可以有多任务,而我还想对用户进行区分,比如会员可以爬行快点,一般的慢点,会员爬的层次多,一般的爬的少,采用的就是类似的方案,不过后台线程调度复杂一点(其实也复杂不了多少)。
只提供思路,不提供具体源码。
本文转自xiaotie博客园博客,原文链接http://www.cnblogs.com/xiaotie/archive/2008/08/17/1269902.html如需转载请联系原作者
xiaotie 集异璧实验室(GEBLAB)