PostThreadMessage在线程中应用(以多线程网站数据采集为例)
PostThreadMessage 顾名思议,向线程中发送消息.下面讲讲我在线程中的应用体会
在Delphi 中,我们大多数人,使用线程的时候,都会用到系统自带的TThread 类,来完成线程的操作。
从TThread 中派生出一个自己的类,然后重载 Execute 函数,其在TThread中被定义成了抽象类(纯虚类C++)。
子类必须重载这个函数.在此函数中,写入你要完成的任务代码. 例如 最简单应用
TCustXXXXThread=class(TThread)
public
procedure Execute();override;
end;
TCustXXXXThread.Execute()
begin
while (not Terminated) do
begin
//do something
end;
//退出后,线程结束
end;
一个线程要完成一项(多项)任务,就涉及到一个最基本也是不可回避的问题,怎样与线程进行通信,把输入参数传递给线程
把线程的运行结果,取出来。实现的方法很多,今天这里重点介绍下。
我的一个软件模块中,需要做数据采集, 把某网站上的数据采集下来。然后,分解提取出需要的数据,整理后,存入数据库
以便于后续处理。网络通信采用了 Indy控件,负责下载网页文本。由于数据量比较大,网站的数据是分页的。我这里也要循环
读取每个网页的文本。数据下载,分解都是耗时操作。很自然的就需要把它们放入线程中运行。以保证主界面不被卡死。
实现的伪代码
while(ture) do
begin
(1)采集网站文本,通知主线程数据下载完毕
(2)分解网站文本,提取数据,
while(ture) do
begin
提取数据记录,发消息给主线程(记录N)分解完毕。
end;
if 采集的网站数据的最后一页 then 退出.
end;
我们开发软件的时候,常常提到系统架构。那么这个模块的架构应该如何那。
1)主要的处理函数全部在线程中运行
2)中间的运行结果要通知主线程,用于界面更新
3)网络程序,必须要考虑到错误处理问题。例如,网站链接超时。
4) 接受主线程命令随时中断采集.
根据此种情况及个人喜好原因。决定采用模拟窗口消息处理的方式,作程序的主框架。以消息的读取,转换,分发
为主线。
代码框架如下:
TWebSample=class(TThread)
private
//..........
protected
procedure WndProc(var AMsg:TMessage); //模拟窗口消息处理
procedure Execute();override; //线程运行的主函数
public
MainWndHande:THandle; //主窗口句柄,用于发消息给主窗口用
end;
TWebSample.Execute()
var
_Msg:tagMsg;
AMessage:TMessage;
begin
//模拟消息驱动,
//在这里起到一个消息泵的作用,用于消息的接受,分析,转发
while (not Terminated) do
begin
if PeekMessage(_Msg,0,WM_USER,WM_USER + 1100,PM_REMOVE) then
begin
AMessage.Msg := _Msg.message;
AMessage.WParam := _Msg.wParam;
AMessage.LParam := _Msg.lParam;
AMessage.Result := 1;
WndProc(AMessage); //消息的转发
if AMessage.Result = 0 then
Break; //退出线程
end
else
Sleep(1); //避免没有消息进来的时候,CPU 占用率过高.具体数值,可以实验测定。
end;
end;
procedure TWebSample.WndProc(var AMsg:TMessage); //模拟窗口消息处理
begin
if AMsg.Msg = MSG_XXXX_01 then
begin
//消息处理
//发消息给主窗体,用于数据更新等操作
SendMesesage(MainWndHand,MSG_MAIN_XXXX,wParam,lParam);
end
else
if AMsg.Msg = MSG_XXX_02 then
begin
//消息处理
end;
end;
//线程介绍完了,下面要谈谈主窗体怎样发消息(命令)给线程的伪代码
procedure TfrmSaleAnaly.btnGoClick(Sender: TObject);
var
ThreadObj: TWebSamplebj;
begin
ThreadObj := TWebSamplebj.Create(TRUE);
ThredObj 对象的初始化
ThredObj.Resume(); //启动线程运行
//发出控制命令给线程
PostThreadMessage(ThreadObj.ThreadID,
MSG_XXXX_01,
wParam,
lParam);
end;
如果投递消息到线程中,没有反应,可以试试下面的方法
procedure PostThreadMessageEh(ThreadID:DWORD;Msg:DWORD;WParam,LParam:Integer);
begin
while(not PostThreadMessage(ThreadID,Msg,WParam,LParam)) do Sleep(5);
end;
MSDN 关于此问题的解决办法.
The thread to which the message is posted must have created a message queue, or else the call to PostThreadMessage fails. Use one of the following methods to handle this situation:
- Call PostThreadMessage. If it fails, call the Sleep function and call PostThreadMessage again. Repeat until PostThreadMessage succeeds.