原需求:
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)