天天看點

從Socket資料處理線程想到的普通Winform資料顯示的應用

5、ThreadHandler,資料獨立線程處理類

對每個不同類型的資料(不同的協定類型),可以用獨立的線程進行處理,這裡封裝了一個基類,用于進行資料獨立線程的處理 

上面的工作原理是這樣的,每次收到資料後,系統把資料扔給獨立線程處理類,處理類放到一個隊列Queue的清單中,每次從中彈出一個來處理,根據不同的協定頭,分派到不同的線程來處理,這樣可以提高響應速度,防止線程之間的阻塞,能夠充分利用系統的資源。

 其實我們還可以把這個思想應用到日常的Winform開發中,有時候我們可能在處理一些比較費時的操作,可能是需要做一部分顯示一部分,類似日常生活中的項目周報、月周報的場景,因為不可能等一個幾年的項目完成後,你才告訴老闆你的工作情況吧。

從Socket資料處理線程想到的普通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);

            }));            

好了,思路是思路,程式是程式,兩者結合就是實踐的證明,采集大量的網站連接配接的時候,在也不會出現主界面停頓或者假死的情況了。下面是我閑暇時間的練筆之作, 貼圖以證方案之可行。

在采集的時候,整個程式再也不會出現假死的情況,你還可以去處理其他工作的。另外,由于涉及了線程的處理工作,你還需要定時檢測處理線程,如果線程有問題,還需要重新開機線程就可以了,這部分是屬于線程檢查優化的部分,不再介紹。

繼續閱讀