利用主线程与子线程间的消息通讯,实现任务处理队列.子线程中创建不会阻塞执行的窗口

 目标:主线程只需发命令(消息)通知子线程需要处理的内容,而不用管任务的处理进度,可以发任意个命令,而子线程依次取出主线程交付的任务来处理.


步骤:
     1.主线程通过PostThreadMessage将命令消息发送给子线程,可以利用wParam与lParam传递需要的参数.
     2.子线程执行代码中建立一个消息循环,PeekMessage从消息队列提取消息,若无消息,则用
MsgWaitForMultipleObjects等待消息或事件对象.

示例代码:

const
   WM_TEST=WM_USER+1;
  
var
   hCloseEvent:THandle;
   ThreadID:DWORD;

type
   TTestThread=class(TThread)
   protected
     procedure Execute;override;
   end;

procedure TTestThread.Execute;
var
   msg:TMsg;
begin
   try
     while True do 
     begin
       if not PeekMessage(msg,0,0,0,PM_REMOVE) then
       begin
         case MsgWaitForMultipleObjects(1,hCloseEvent,False,INFINITE,QS_ALLINPUT) of
           WAIT_OBJECT_0: Break;//hCloseEvent事件已经有效,故结束
           WAIT_OBJECT_0+1:Continue;//消息队列中有消息存在
           else Break;//其它情况,WM_FAILED或WAIT_ABANDONED_0,表明已经出错,故结束
         end;
       end else
       begin
         if WaitForSingleObject(hCloseEvent,0)<>WAIT_TIMEOUT then Break;
         case msg.message of
           WM_TEST:
           begin
             //...接到主线程发过来的命令,作相应处理
             //...处理完之后,也可以用消息通知主线程
           end;
         end;
       end;  
     end;
   finally
     //...作善后处理
   end;
end;

procedure TForm1.btnCmdClick(Sender: TObject);
begin
   //给子线程发送消息
   PostThreadMessage(ThreadID,WM_TEST,0,0);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   //建立事件对象以及子线程,并保存子线程的ThreadID,用以调用PostThreadMessage
   hCloseEvent:=CreateEvent(nil,True,False,0);
   ThreadID:=TTestThread.Create(False).ThreadID;
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
   SetEvent(hCloseEvent);
   CloseHandle(hCloseEvent);
end;

procedure TForm1.btnStopClick(Sender: TObject);
begin
   SetEvent(hCloseEvent);
end;


可以参考”C# 对 Windows 窗体控件进行线程安全调用“一文来看。

C# 在子线程中创建不会阻塞执行的窗口

在做网络连接程序的时候碰到一个问题:每当连接到来时,都创建一个新的接收线程,而该接收线程在接收到消息时,可以创建一个新的对话窗口,而该窗口不能阻塞该接收线程的下一轮消息的接收,而且该接收线程还要把接收到的消息显示在该窗口上

Form.ShowDialog();方法弹出模态对话框,而模态对话框会阻塞后面代码的执行,导致接收线程无法继续执行(除非该模态窗口被关闭)

刚开始想到的解决办法,就是:通过Form.Show();方法,显示非模态的窗口

非模态对话框有几个问题:

(1) 生命期限问题:由于是在线程的一轮执行中创建的,所以,该对象可能在该轮结束后被垃圾回收机制自动清除掉;

(2) 模态对话框没有系统消息机制,需要自己写;(好像是这样的,没用过...)

(3) 在代码中尝试了一下,没有创建消息机制,只是显示窗口。但该窗口处于假死状态,根本没法用。

由于上述的几个困难,只能另觅它途了...

成功实验-----异步委托

在看其他文章的时候,偶然看到Control.Invoker()方法,该方法可执行自定义或系统定义的委托。

突然想起来,委托可以通过用Invoke()方法同步执行(阻塞执行),而通过BeginInvoke()方法异步执行,如果是异步执行的话,应该是不会阻塞线程的执行吧...

于是,做了如下的例子:

注意事项:

通过Invoke()/BeginInvoke()调用委托,必须由已经出现的控件来调用,否则会出现错误提示:“未经处理的异常:  System.InvalidOperationException: 在创建窗口句柄之前,不能在控件上调用 Invoke 或 BeginInvoke。”

(1) 打开窗口代码:

[csharp]  view plain copy
  1. void openNewForm()  
  2. {  
  3.     Form2 newForm = new Form2();  
  4.     newForm.ShowDialog();  
  5. }  
(2) 线程入口函数

[csharp]  view plain copy
  1. //线程入口函数  
  2. void _threadProc()  
  3. {  
  4.     //定义一个委托实例,该实例执行打开窗口代码  
  5.     MethodInvoker mi = new MethodInvoker(openNewForm);  
  6.     BeginInvoke(mi);  
  7.   
  8.     //如果没有阻塞的话,该段代码应该可执行  
  9.     Console.WriteLine("新打开的窗口没有阻塞之后的执行");  
  10.     Console.ReadLine();  
  11. }  
(3) 创建,并执行线程

[csharp]  view plain copy
  1. Thread newThread = new Thread(new ThreadStart(_threadProc));  
  2. newThread.Start();  
(4) 设置项目的输出类型为:控制台应用程序(这样执行的时候会同时弹出控制台和窗口)

执行结果如下:

可见,新打开的模式窗口并没有阻塞线程的执行!

因为,如果是同步调用的话,在Form2窗口关闭之前,“新打开的窗口没有阻塞之后的执行”就不会有输出。

利用主线程与子线程间的消息通讯,实现任务处理队列.子线程中创建不会阻塞执行的窗口_第1张图片




你可能感兴趣的:(利用主线程与子线程间的消息通讯,实现任务处理队列.子线程中创建不会阻塞执行的窗口)