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.
原文地址:https://www.cnblogs.com/lwm8246/p/2199994.html