从Socket数据处理线程想到的普通Winform数据显示的应用
在前面介绍过Socket编程的文章中,有一篇是《
Socket开发探秘--基类及公共类的定义
》,其中介绍了一个独立线程处理类,专门在一个独立的线程中处理Socket的数据包的。摘录前面的内容介绍一下:
其实我们还可以把这个思想应用到日常的Winform开发中,有时候我们可能在处理一些比较费时的操作,可能是需要做一部分显示一部分,类似日常生活中的项目周报、月周报的场景,因为不可能等一个几年的项目完成后,你才告诉老板你的工作情况吧。 借鉴Socket的数据处理方式,我在Winform程序中运用了这种数据处理方式,如我在采集赶集网的数据的时候,可以把采集到的部分数据扔给系统中的数据独立处理线程,让他们爱怎么显示就怎么显示,程序不中断,继续乐此不彼的去采集内容去,然后继续这样做(每采集一部分仍出去一部分),直到采集完毕。 上面的是独立线程处理的基类,下面我们用一个子类继承他,方便代码逻辑的剥离封装: 在下面的代码中,我根据不同的Table表内容类型,放到不同的函数中进行处理,以便实现不同的显示方式。 下面代码是表的不同类型的枚举类和预处理数据格式定义。 在实际的赶集网采集程序中,我需要每采集一个链接的内容后,就处理并显示,因此示例代码如下所示: 好了,思路是思路,程序是程序,两者结合就是实践的证明,采集大量的网站连接的时候,在也不会出现主界面停顿或者假死的情况了。下面是我闲暇时间的练笔之作, 贴图以证方案之可行。
、ThreadHandler,数据独立线程处理类
对每个不同类型的数据(不同的协议类型),可以用独立的线程进行处理,这里封装了一个基类,用于进行数据独立线程的处理
上面的工作原理是这样的,每次收到数据后,系统把数据扔给独立线程处理类,处理类放到一个队列Queue的列表中,每次从中弹出一个来处理,根据不同的协议头,分派到不同的线程来处理,这样可以提高响应速度,防止线程之间的阻塞,能够充分利用系统的资源。
代码
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));
}
代码
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
<
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));
}
}
代码
///
<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);
}));
}
在采集的时候,整个程序再也不会出现假死的情况,你还可以去处理其他工作的。另外,由于涉及了线程的处理工作,你还需要定时检测处理线程,如果线程有问题,还需要重启线程就可以了,这部分是属于线程检查优化的部分,不再介绍。