本帖最后由 FlashDance 于 2014-11-28 19:37:17 编辑

解决方案 »

  1.   

    GetFileThread.Terminate;
    Terminate只是设置一个变量为True。
    procedure TThread.Terminate;
    begin
      FTerminated := True;
    end;
    如果在你的线程函数中没有处理这个变量跳出函数,那么对于你来说这句是没有任何作用的。
    一般典型的线程实现是这样的
    procedure TMyThread.Execute;
    begin
      while not Terminated do
      begin
        //balabala
      end;
    end;
    当Terminate被执行后Terminated即为True,此时循环体会跳出循环,当然,前提是balabala中的代码不能阻塞。
      

  2.   


    这个肯定是有的,不然Sleep(5000)也不可能受之影响了,问题是【CreateProcess】、【MsgWaitForMultipleObjects】与线程冲突了
      

  3.   

    你看看是不是你线程中执行进程阻塞了?
    和那没关系,Sleep(5000)本来就是5秒的阻塞
    没有Sleep(5000),线程在GetFileThread.Terminate时很快就结束了,反而无法测试单独执行线程完全正常,CreateProcess才造成的影响
    现在的问题不是线程,也不是外部进程,是它们的内核对象产生了干扰
    估计是MsgWaitForMultipleObjects检测内核对象时,受到CreateProcess句柄影响了
    MsgWaitForMultipleObjects的句柄是H,它分明是GetFileThread.Handle和外部程序无关的
      

  4.   

    用WaitForSingleObject竟然结果也是一样的,受到了CreateProcess的干扰,H明明是GetFileThread.Handle,和CreateProcess无关啊
    var
      H:THandle;
    begin
      H:=GetFileThread.Handle;
      GetFileThread.Terminate;
      WaitForSingleObject(H,INFINITE);
      ShowMessage('Finish');
    end;尽管H是局部变量在此代码段优先级最高,没什么问题
    为了防止意外,还是将它改为其它名字,如THTH,结果仍然相同WaitForSingleObject和MsgWaitForMultipleObjects均会受到CreateProcess的干扰
      

  5.   

    搞个最简重现的工程吧,光看你这么描述,实在解决不了。
    搞了保卫战最简的重现工程,这次彻底凌乱了,情况和上面不同了,这次是WaitForSingleObject完全不起任何作用:
    unit UnitMain;interfaceuses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;type
      TFormMain = class(TForm)
        btn1: TButton;
        procedure btn1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      FormMain: TFormMain;implementation{$R *.dfm}uses UnitMyThread;const
      ClientExeName='Hash.exe';var
      MyThread1:TMyThread;
      H:array[0..0] of THandle;procedure TFormMain.FormCreate(Sender: TObject);
    var
      StartupInfo: TStartUpInfo;
      ProcessInfo: TProcessInformation;
    begin
      MyThread1:=TMyThread.Create;
      H[0]:=MyThread1.Handle;  {FillChar(StartupInfo, SizeOf(StartupInfo), 0);
      with StartupInfo do
      begin
        cb := SizeOf(StartupInfo);
        dwFlags := StartF_UsesTDHandles or STARTF_USESHOWWINDOW;
        lpTitle := nil;
        wShowWindow := SW_Show;
      end;;
      if not CreateProcess(PChar(ExtractFilePath(ParamStr(0)) + ClientExeName), nil,
        nil, nil, True, DETACHED_PROCESS, nil, nil, StartupInfo, ProcessInfo) then
      begin
        Application.MessageBox('无法启动客户端', '找不到客户端程序', MB_ICONERROR);
      end;}
    end;procedure TFormMain.btn1Click(Sender: TObject);
    begin
      MyThread1.Terminate;
      //MsgWaitForMultipleObjects  完全不起作用了
      ShowMessage(IntToStr(MsgWaitForMultipleObjects(1,H,True,INFINITE,QS_ALLINPUT)-WAIT_OBJECT_0));
    end;
    end.unit UnitMyThread;interfaceuses
      System.Classes,System.SysUtils
    ;type
      TMyThread = class(TThread)
      private
        { Private declarations }
      protected
        procedure Execute; override;
      public
        constructor Create;
      end;implementation{ TMyThread }uses UnitMain;constructor TMyThread.Create;
    begin
      inherited Create(False);
      FreeOnTerminate := True;
    end;procedure TMyThread.Execute;
    var
      I:Integer;
    begin
      I:=0;
      while not Terminated do
      begin
        Sleep(100);
        Inc(I);
        Synchronize(
          procedure
          begin
            FormMain.Caption:=IntToStr(I);
          end);
      end;
      Sleep(2000);
    end;end.
      

  6.   

    你这个代码是啥问题我不清楚,但是你上个代码的问题基本是因为这样。首先你在线程中使用Synchronize同步到主线程进行更新Form的Caption,然后你又在主线程中阻塞等待线程结束,这样会造成死锁。
    按下按钮死锁是这样发生的:
    线程等待同步到主线程更新Caption,此时你主线程处理按下按钮,主线程永久等待在WaitFor,而线程又永久等待同步到主线,导致死锁。
      

  7.   

    unit UnitMain;interfaceuses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
      System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;type
      TFormMain = class(TForm)
        btn1: TButton;
        procedure btn1Click(Sender: TObject);
        procedure FormCreate(Sender: TObject);
      private
        { Private declarations }
      public
        { Public declarations }
      end;var
      FormMain: TFormMain;
      I: Integer;implementation{$R *.dfm}uses UnitMyThread;const
      ClientExeName = 'Hash.exe';var
      MyThread1: TMyThread;
      H: array [0 .. 0] of THandle;procedure TFormMain.FormCreate(Sender: TObject);
    var
      StartupInfo: TStartUpInfo;
      ProcessInfo: TProcessInformation;
    begin
      MyThread1 := TMyThread.Create;
      H[0] := MyThread1.Handle;  FillChar(StartupInfo, SizeOf(StartupInfo), 0);
      with StartupInfo do
      begin
        cb := SizeOf(StartupInfo);
        dwFlags := StartF_UsesTDHandles or STARTF_USESHOWWINDOW;
        lpTitle := nil;
        wShowWindow := SW_Show;
      end;;
      if not CreateProcess(PChar(ExtractFilePath(ParamStr(0)) + ClientExeName), nil,
        nil, nil, True, DETACHED_PROCESS, nil, nil, StartupInfo, ProcessInfo) then
      begin
        Application.MessageBox('无法启动客户端', '找不到客户端程序', MB_ICONERROR);
      end;
    end;procedure TFormMain.btn1Click(Sender: TObject);
    begin
      MyThread1.Terminate;
      ShowMessage(IntToStr(MsgWaitForMultipleObjects(1, H, True, INFINITE,
        QS_ALLINPUT) - WAIT_OBJECT_0));
      ShowMessage(IntToStr(I));
    end;end.unit UnitMyThread;interfaceuses
      System.Classes, System.SysUtils;type
      TMyThread = class(TThread)
      private
        { Private declarations }
      protected
        procedure Execute; override;
      public
        constructor Create;
      end;implementation{ TMyThread }uses UnitMain;constructor TMyThread.Create;
    begin
      inherited Create(False);
      FreeOnTerminate := True;
    end;procedure TMyThread.Execute;
    var
      I: Integer;
    begin
      I := 0;
      while not Terminated do
      begin
        Sleep(100);
        Inc(I);
      end;
      Sleep(2000);
      UnitMain.I := I;
    end;end.果然是这样的,【CreateProcess】、【MsgWaitForMultipleObjects】与【线程】之间的冲突也没有了还有2个问题:
    A:
    但这样写才行:MsgWaitForMultipleObjects(1, H, True, INFINITE,QS_ALLINPUT)
    百度到其它的写法(第3个参数是False):MsgWaitForMultipleObjects(1, H, False, INFINITE,QS_ALLINPUT)是不行的,难道
    这种是错误的?
    B:那么如果线程中要更新界面,主线程中又要用MsgWaitForMultipleObjects判断线程的结束,应该如何处理呢?
    非常感谢
      

  8.   

    A:
    这个参数解释
    bWaitAll [in]    If this parameter is TRUE, the function returns when the states of all objects in the pHandles array have been set to signaled and an input event has been received. If this parameter is FALSE, the function returns when the state of any one of the objects is set to signaled or an input event has been received. In this case, the return value indicates the object whose state caused the function to return.你一个等待对象的话,True和False是没有区别的。B:
    这种情况可以自定义消息使用PostMessage。
      

  9.   

    A:我也是理解等待一个对象没有区别,但上述代码用False等待一个对象却不行B:我试着用PostMessage来操作,在线程中也只有这一行代码用来启动exe,无意发现,就是在线程中用PostMessage启动exe,造成了这个结果:
    “MsgWaitForMultipleObjects就只能在外部程序关闭,并且线程结束时,才能返回,“H”明明是线程的句柄,和CreateProcess没关系,但却受它影响了”真是不知如何解决了
    unit UnitMyThread;interfaceuses
      System.Classes, System.SysUtils, Winapi.Messages, Winapi.Windows;type
      TMyThread = class(TThread)
      private
        { Private declarations }
      protected
        procedure Execute; override;
      public
        constructor Create;
      end;implementation{ TMyThread }uses UnitMain;constructor TMyThread.Create;
    begin
      inherited Create(False);
      FreeOnTerminate := True;
    end;procedure TMyThread.Execute;
    begin
      PostMessage(FormMain.Handle, WM_USER + 5000, 0, 0);
    end;end.unit UnitMain;interfaceuses
      Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
      System.Classes, Vcl.Graphics,
      Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;type
      TFormMain = class(TForm)
        btn1: TButton;
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
        procedure btn1Click(Sender: TObject);
      private
        { Private declarations }
        procedure RunExe(var Msg: TMessage); message WM_USER + 5000;
      public
        { Public declarations }
      end;var
      FormMain: TFormMain;implementation{$R *.dfm}uses UnitMyThread;const
      ClientExeName = 'Hash.exe';var
      MyThread1: TMyThread;
      H: array [0 .. 0] of THandle;procedure TFormMain.btn1Click(Sender: TObject);
    begin
      MyThread1 := TMyThread.Create;
      H[0] := MyThread1.Handle;
    end;procedure TFormMain.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      MyThread1.Terminate;
      ShowMessage(IntToStr(MsgWaitForMultipleObjects(1, H, True, INFINITE,
        QS_ALLINPUT) - WAIT_OBJECT_0));
    end;procedure TFormMain.RunExe(var Msg: TMessage);
    var
      StartupInfo: TStartUpInfo;
      ProcessInfo: TProcessInformation;
    begin
      FillChar(StartupInfo, SizeOf(StartupInfo), 0);
      with StartupInfo do
      begin
        cb := SizeOf(StartupInfo);
        dwFlags := StartF_UsesTDHandles or STARTF_USESHOWWINDOW;
        lpTitle := nil;
        wShowWindow := SW_Show;
      end;
      if not CreateProcess(PChar(ExtractFilePath(ParamStr(0)) + ClientExeName), nil,
        nil, nil, True, DETACHED_PROCESS, nil, nil, StartupInfo, ProcessInfo) then
        Application.MessageBox('无法启动客户端', '找不到客户端程序', MB_ICONERROR);
      FormMain.Close;
    end;end.真是麻烦了,求指教
      

  10.   

    本意是想在由线程处理一些数据,处理成功后,由线程启动一个EXE,线程再继续其它操作。
    主线程在子线程结束后就退出。那个EXE并不需要退出。
      

  11.   

    unit Unit1;interfaceuses
      Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls;const
      WM_EXECUTE = WM_USER + 5000;type
      TMyThread = class(TThread)
      protected
        procedure Execute(); override;
      public
        constructor Create();
      end;  TForm1 = class(TForm)
        btn1: TButton;
        procedure FormClose(Sender: TObject; var Action: TCloseAction);
        procedure btn1Click(Sender: TObject);
      private
        FMyThread: TMyThread;
        procedure RunExe(var Msg: TMessage); message WM_EXECUTE;
      public  end;var
      Form1: TForm1;implementation{$R *.dfm}const
      ClientExeName = 'notepad.exe';procedure TForm1.btn1Click(Sender: TObject);
    begin
      FMyThread := TMyThread.Create;
    end;procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
    begin
      FMyThread.Free; //请仔细看TThread.Destroy(重点WaitFor)
      ShowMessage('线程已经执行完毕');
    end;
    { TMyThread }constructor TMyThread.Create;
    begin
      inherited Create(False);
    end;procedure TMyThread.Execute;
    begin
      PostMessage(Form1.Handle, WM_EXECUTE, 0, 0);
    end;procedure TForm1.RunExe(var Msg: TMessage);
    var
      StartupInfo: TStartUpInfo;
      ProcessInfo: TProcessInformation;
    begin
      FillChar(StartupInfo, SizeOf(StartupInfo), 0);
      with StartupInfo do
      begin
        cb := SizeOf(StartupInfo);
        dwFlags := StartF_UsesTDHandles or STARTF_USESHOWWINDOW;
        lpTitle := nil;
        wShowWindow := SW_Show;
      end;
      if not CreateProcess(PChar(ExtractFilePath(ParamStr(0)) + ClientExeName), nil,
        nil, nil, True, DETACHED_PROCESS, nil, nil, StartupInfo, ProcessInfo) then
        Application.MessageBox('无法启动客户端', '找不到客户端程序', MB_ICONERROR);
      Self.Close;
    end;end.
      

  12.   

    需要用到FreeOnTerminate := True;的时候还是很多
    现在只好这样写了:  MyThread1.Terminate;
      while not MyThread1.Finished do
        Application.ProcessMessages;非常感谢pathletboy的帮助
      

  13.   


    没必要这么低效死循环判断标志,直接重载TThread.Destroy,在Destroy中最后PostMessage通知主窗体
      

  14.   


    没必要这么低效死循环判断标志,直接重载TThread.Destroy,在Destroy中最后PostMessage通知主窗体判断子线程结束前后都有代码,无法独立出来,不方便接收PostMessage啊
      

  15.   


    没必要这么低效死循环判断标志,直接重载TThread.Destroy,在Destroy中最后PostMessage通知主窗体判断子线程结束前后都有代码,无法独立出来,不方便接收PostMessage啊死等后面的全丢消息处理就可以了。
      

  16.   


    没必要这么低效死循环判断标志,直接重载TThread.Destroy,在Destroy中最后PostMessage通知主窗体判断子线程结束前后都有代码,无法独立出来,不方便接收PostMessage啊死等后面的全丢消息处理就可以了。
    好的,非常感谢