5、ThreadHandler,資料獨立線程處理類
對每個不同類型的資料(不同的協定類型),可以用獨立的線程進行處理,這裡封裝了一個基類,用于進行資料獨立線程的處理
上面的工作原理是這樣的,每次收到資料後,系統把資料扔給獨立線程處理類,處理類放到一個隊列Queue的清單中,每次從中彈出一個來處理,根據不同的協定頭,分派到不同的線程來處理,這樣可以提高響應速度,防止線程之間的阻塞,能夠充分利用系統的資源。
其實我們還可以把這個思想應用到日常的Winform開發中,有時候我們可能在處理一些比較費時的操作,可能是需要做一部分顯示一部分,類似日常生活中的項目周報、月周報的場景,因為不可能等一個幾年的項目完成後,你才告訴老闆你的工作情況吧。
借鑒Socket的資料處理方式,我在Winform程式中運用了這種資料處理方式,如我在采集趕集網的資料的時候,可以把采集到的部分資料扔給系統中的資料獨立處理線程,讓他們愛怎麼顯示就怎麼顯示,程式不中斷,繼續樂此不彼的去采集内容去,然後繼續這樣做(每采集一部分仍出去一部分),直到采集完畢。
public class ThreadHandler<T>
{
/// <summary>
/// 處理資料線程
/// </summary>
Thread _Handlehread = null;
private string _ThreadName = "";
private Fifo<T> _DataFifo = new Fifo<T>();
/// 線程名字
public string ThreadName
{
get { return _ThreadName; }
set { _ThreadName = value; }
}
/// 接收處理資料
/// <param name="data"></param>
public virtual void AppendData(T data)
if (data != null)
_DataFifo.Append(data);
/// 資料處理
protected virtual void DataThreadHandle()
try
{
while (true)
{
T data = _DataFifo.Pop();
DataHandle(data);
}
}
catch(Exception ex)
LogHelper.Error(ex);
public virtual void DataHandle(T data)
/// 開始資料處理線程
public virtual void StartHandleThread()
if (_Handlehread == null)
_Handlehread = new Thread(new ThreadStart(DataThreadHandle));
_Handlehread.IsBackground = true;
_Handlehread.Start();
LogHelper.Info(string.Format("[ThreadHandler] 線程->{0}啟動。。。。。。", _ThreadName));
上面的是獨立線程處理的基類,下面我們用一個子類繼承他,友善代碼邏輯的剝離封裝:
在下面的代碼中,我根據不同的Table表内容類型,放到不同的函數中進行處理,以便實作不同的顯示方式。
public class TestDataHandleThread : ThreadHandler<PreData>
public TestDataHandleThread()
base.ThreadName = "測試資料操作處理線程";
public override void DataHandle(PreData data)
if (data.Key == KeyType.PostAticle)
if (!string.IsNullOrEmpty(data.Content.TableName))
{
ThreadPool.QueueUserWorkItem(new WaitCallback(Portal.gc.MainDialog.DisplayForm), data.Content);
}
else if (data.Key == KeyType.ContactInfo)
ThreadPool.QueueUserWorkItem(new WaitCallback(Portal.gc.MainDialog.DisplayContactForm), data.Content);
catch (Exception ex)
LogHelper.Error("[TestDataHandleThread] 測試資料操作處理線程異常:{0}" + ex.ToString());
}
下面代碼是表的不同類型的枚舉類和預處理資料格式定義。
public enum KeyType{PostAticle, ContactInfo};
/// <summary>
/// 預處理的資料
/// </summary>
public class PreData
private KeyType key;
private DataTable content;
public KeyType Key
get { return key; }
set { key = value; }
public DataTable Content
get { return content; }
set { content = value; }
public PreData(KeyType key, DataTable data)
this.key = key;
this.content = data;
在實際的趕集網采集程式中,我需要每采集一個連結的内容後,就處理并顯示,是以示例代碼如下所示:
/// 擷取網站釋出内容,并添加到線程進行處理
/// <param name="itemDict"></param>
/// <param name="regexDict"></param>
private void GetContent(Dictionary<string, string> itemDict)
foreach (string key in itemDict.Keys)
DataTable dt = new DataTable(key);
//标題解析,省略N行代碼
//内容解析,省略N+N行代碼
//添加到線程進行處理
Portal.gc.MainDialog.AddData(new PreData(KeyType.PostAticle, dt));
/// 添加消息資料,根據不同的消息類型分派到不同的線程處理
/// <param name="data">消息資料</param>
public void AddData(PreData data)
_testDataThread.AppendData(data);
/// 采用多線程方式顯示内容資料
public void DisplayForm(object table)
DataTable data = table as DataTable;
FrmContent content = FindDocument(data.TableName) as FrmContent;
if (content == null)
content = new FrmContent();
content.TabText = data.TableName;
content.Text = data.TableName;
}
this.Invoke(new MethodInvoker(delegate()
content.BindData(data, data.TableName);
content.Show(this.dockPanel);
}));
好了,思路是思路,程式是程式,兩者結合就是實踐的證明,采集大量的網站連接配接的時候,在也不會出現主界面停頓或者假死的情況了。下面是我閑暇時間的練筆之作, 貼圖以證方案之可行。
在采集的時候,整個程式再也不會出現假死的情況,你還可以去處理其他工作的。另外,由于涉及了線程的處理工作,你還需要定時檢測處理線程,如果線程有問題,還需要重新開機線程就可以了,這部分是屬于線程檢查優化的部分,不再介紹。