线程启动后界面假死的故障排除

作者: yherxl

昨天把一个在服务器和客户端同步数据的过程改为线程方式, 就是为了让界面不再假死掉, 每次循环中通过synchonize过程来同步界面上的显示. 当时是成功的, 后来又修改了一些地方.

但今天重新运行,发现界面又假死了, 跟不用线程一样了. 据说线程引用的任何主线程的元素都要同步访问,于是把所有过程都加上同步, 还是不行.

Ok, 那就来个最简化的例子, 去掉所有主线程的元素, 也去掉所有同步过程, 只是一个for循环, 进行2000次, 每次暂停20ms用来降低cpu占用率, 这下总行了吧, 一运行还是冻住了, 看来问题不在线程当中

于是查看启动线程的代码

  thd:=UpLoadThread.Create;

  if (thd.WaitFor = 0) then
    showmessage('上传成功')
  else
    showmessage('上传失败');
  Msg:=thd.SumMsg;

  FreeAndNil(thd);

再次来个最简化, 只保留第一行, 嘿, OK了, 看来问题就是出在Waitfor上面了, 我这里用Waitfor是为了获得线程的返回值(ReturnValue), 但是有一个缺点,当执行线程的时候主程序也会停下来等待线程的结束,主程序会暂停响应

当我再加上FreeAndNil语句后, 又出现问题, 线程Create后, 直接被Free掉了, 没有进入Excute过程. 看来不用waitfor后, 就只能使用FreeOnTerminate来自动释放线程对象了

在网上发现下面一篇文章, 很有启发, 使用MsgWaitForMultipleObjects来获得类似waitfor的效果,但不会阻塞主线程, Waitfor的ReturnValue当然不能直接获得, 但可以通过线程的公用变量来模拟,即thd.SumMsg变量. 也可以通过API函数GetExitCodeThread来获得ReturnValue.
(实际上, Waitfor函数内部就是这么实现的, 但是少了ProcessMessage就造成界面冻住)

begin
  thd:=UpLoadThread.Create;
  H := Thd.Handle;

  repeat
    W := MsgWaitForMultipleObjects(1, H, False, INFINITE, QS_ALLINPUT);
    Application.ProcessMessages;
  until (W = WAIT_OBJECT_0) or (W = WAIT_FAILED);
  GetExitCodeThread(H, ResultCode);  //ResultCode为LongWord类型

  Msg:=thd.SumMsg;

  FreeAndNil(thd);
end;

-----------------------------------以下为转贴--------------------------------------




MyThread:=TMyThread.Create(False);
如何判断线程MyThread已执行完毕?
因为程序中有个事件必须等某线程完成后才执行
说明中说可以用ReturnValue,但我感觉这个值一直是0,没有变化啊

----------------------

用MyThread.Waitfor或者WaitForSingleObject(MyThread.Handle, INFINITE)

----------------------

把你要執行的事件放到線程的Excute中
MyThread.Execute
 ....
  FreeOnTerminate:=True;
  Onterminate:=你要執行的事件;

----------------------

type
  TMyThread = class(TThread)
  protected
    procedure Execute; override;
  end;
{ TMyThread }

procedure TMyThread.Execute;
begin
  FreeOnTerminate := False;

  Sleep(5000);
end;



procedure TForm3.Button1Click(Sender: TObject);
var
  T : TMyThread;
begin
  T := TMyThread.Create(False);
  try
    T.WaitFor;
    ShowMessage('执行完了');
  finally
    T.Free;
  end;
end;

procedure TForm3.Button2Click(Sender: TObject);
var
  T : TMyThread;
begin
  T := TMyThread.Create(False);
  try
    if WaitForSingleObject(T.Handle, INFINITE) = WAIT_OBJECT_0 then
      begin
      ShowMessage('执行完了');
      end;
  finally
    T.Free;
  end;
end;

 //方法是可以的,但是有一个缺点,当执行线程的时候主程序也会停下来等待线程的结束,主程序会暂

停响应,这样调用多线程就没有意义了。

----------------------

用Onterminate事件当然可以,但是这时线程并没有结束,仅仅表示Execute方法调用

结束了,而用WaitforSingleObject就不同了

----------------------

使用Onterminate事件固然有它的局限性,因为触发Onterminate事件的时候线程还没有完全结束,用它的优点是线程执行的同时,主程序也可以继续执行,这也是多线程的优点之一。
要实现线程完全结束才触发主程序继续执行的话,主线程中执行WaitFor是比较好的实现方法,然而主线程调用WaitFor必须用MsgWaitForMultipleObjects来等待线程,而不是WaitforSingleObject。因为在线程函数Execute中可能调用Synchronize处理同步方法,而同步方法是在主线程中执行的,如果用WaitForSingleObject等待的话,则主线程在这里被挂起,同步方法无法执行,导致线程也被挂起,于是发生死锁。
如果必须要用WaitForSingleObject,应该另开线程来调用WaitForSingleObject,而不是在主线程。
以上是在下愚见,见笑了!哈!

----------------------

这样可以使得界面不"死",但是由于ProcessMessages的缘故,不能保证某段代码不被执行,除非设置一个标志..

procedure TForm1.Button3Click(Sender: TObject);
var
  T: TMyThread;
  H: THandle;
  W: DWord;
begin
  T := TMyThread.Create(False);
  H := T.Handle;

  repeat
    W := MsgWaitForMultipleObjects(1, H, False, INFINITE, QS_ALLINPUT);
    Application.ProcessMessages;
  until (W = WAIT_OBJECT_0) or (W = WAIT_FAILED);

  ShowMessage('执行完了');

  T.Free;
end;

----------------------

//等待一个线程结束的关键代码。绝对可行
  var
    i:dword;
    isquit:boolean;
begin
  if assigned(AThread) then
  begin
    isquit:=GetExitCodeThread(AThread.handle,i);
    if isquit then
    begin
      if i=STILL_ACTIVE then
      begin
        WaitForSingleObject(AThread.Handle,INFINITE
        );
      end
    end;
  end;
end;  

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值