zoukankan      html  css  js  c++  java
  • 从Socket数据处理线程想到的普通Winform数据显示的应用

    在前面介绍过Socket编程的文章中,有一篇是《Socket开发探秘--基类及公共类的定义》,其中介绍了一个独立线程处理类,专门在一个独立的线程中处理Socket的数据包的。摘录前面的内容介绍一下:

    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>();

            
    /// <summary>
            
    /// 线程名字
            
    /// </summary>
            public string ThreadName
            {
                
    get { return _ThreadName; }
                
    set { _ThreadName = value; }
            }

            
    /// <summary>
            
    /// 接收处理数据
            
    /// </summary>
            
    /// <param name="data"></param>
            public virtual void AppendData(T data)
            {
                
    if (data != null)
                    _DataFifo.Append(data);
            }

            
    /// <summary>
            
    /// 数据处理
            
    /// </summary>
            protected virtual void DataThreadHandle()
            {
                
    try
                {
                    
    while (true)
                    {
                        T data 
    = _DataFifo.Pop();
                        DataHandle(data);
                    }
                }
                
    catch(Exception ex)
                {
                    LogHelper.Error(ex);
                }
            }

            
    /// <summary>
            
    /// 数据处理
            
    /// </summary>
            
    /// <param name="data"></param>
            public virtual void DataHandle(T data)
            {
            }

            
    /// <summary>
            
    /// 开始数据处理线程
            
    /// </summary>
            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)
            {
                
    try
                {
                    
    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)
                    {
                        
    if (!string.IsNullOrEmpty(data.Content.TableName))
                        {
                            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;
            }
        }

     在实际的赶集网采集程序中,我需要每采集一个链接的内容后,就处理并显示,因此示例代码如下所示:

    代码
            /// <summary>
            
    /// 获取网站发布内容,并添加到线程进行处理
            
    /// </summary>
            
    /// <param name="itemDict"></param>
            
    /// <param name="regexDict"></param>
            private void GetContent(Dictionary<stringstring> itemDict)
            {
                
    foreach (string key in itemDict.Keys)
                {
                    DataTable dt 
    = new DataTable(key);

                    
    //标题解析,省略N行代码
                    
    //内容解析,省略N+N行代码

                    
    //添加到线程进行处理
                    Portal.gc.MainDialog.AddData(new PreData(KeyType.PostAticle, dt));
                }
            }
    代码
            /// <summary>
            
    /// 添加消息数据,根据不同的消息类型分派到不同的线程处理
            
    /// </summary>
            
    /// <param name="data">消息数据</param>
            public void AddData(PreData data)
            {
                _testDataThread.AppendData(data);
            }

            
    /// <summary>
            
    /// 采用多线程方式显示内容数据
            
    /// </summary>
            
    /// <param name="data"></param>
            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);
                }));            
            }

    好了,思路是思路,程序是程序,两者结合就是实践的证明,采集大量的网站连接的时候,在也不会出现主界面停顿或者假死的情况了。下面是我闲暇时间的练笔之作, 贴图以证方案之可行。

     

    在采集的时候,整个程序再也不会出现假死的情况,你还可以去处理其他工作的。另外,由于涉及了线程的处理工作,你还需要定时检测处理线程,如果线程有问题,还需要重启线程就可以了,这部分是属于线程检查优化的部分,不再介绍。

    主要研究技术:代码生成工具、会员管理系统、客户关系管理软件、病人资料管理软件、Visio二次开发、酒店管理系统、仓库管理系统等共享软件开发
    专注于Winform开发框架/混合式开发框架Web开发框架Bootstrap开发框架微信门户开发框架的研究及应用
      转载请注明出处:
    撰写人:伍华聪  http://www.iqidi.com 
        
  • 相关阅读:
    字节跳动在 Go 网络库上的实践
    TCP报文段的首部格式 20字节的固定首部
    网易公开课 文件描述符 索引
    网易新闻App架构重构实践:DDD正走向流行
    货 | 携程是如何做AB实验分流的
    Pusher Channels Protocol | Pusher docs https://pusher.com/docs/channels/library_auth_reference/pusher-websockets-protocol
    避免重复提交?分布式服务的幂等性设计! 架构文摘 今天 点击蓝色“架构文摘”关注我哟 加个“星标”,每天上午 09:25,干货推送! 来源:https://www.cnblogs.com/QG-whz/p/10372458.html 作者:melonstreet
    前置时间(Lead Time),也称前置期、备货周期
    滴滴业务研发的精益实践
    DevOps运动的缘起 将DevOps想象为一种编程语言里面的一个接口,而SRE类实现了这个接口
  • 原文地址:https://www.cnblogs.com/wuhuacong/p/1664279.html
Copyright © 2011-2022 走看看