解决方案 »
- 在线求助!鼠标放到TToolButton上时,字体为什么会变大,并且出现省略号!
- TreeView里OverlayIndex在节点Move后的问题
- 如何控制Textout函数输出的文字的颜色和背景
- 有关父窗体与子窗体,大侠帮忙
- Timer并不简单,请大家进来讨论一下
- 自定义FastReport的格式问题
- 在win2000字体显示正常,但在win98显示不正常,反之亦然,如何解决,难道只能在98和win200分别设置字体编译吗
- 拨号后从远程win2000服务器上下载文件
- 如何用Delphi制作虚拟桌面
- 大家赶紧帮我一把,怎么写一个模拟NT的 net send 命令的程序吧!我实在是没办法了!
- idFTPClient经常性卡死是什么原因,请高人指点!
- 初学delphi 请问各位用哪个版本的教程好?
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中的代码不能阻塞。
这个肯定是有的,不然Sleep(5000)也不可能受之影响了,问题是【CreateProcess】、【MsgWaitForMultipleObjects】与线程冲突了
和那没关系,Sleep(5000)本来就是5秒的阻塞
没有Sleep(5000),线程在GetFileThread.Terminate时很快就结束了,反而无法测试单独执行线程完全正常,CreateProcess才造成的影响
现在的问题不是线程,也不是外部进程,是它们的内核对象产生了干扰
估计是MsgWaitForMultipleObjects检测内核对象时,受到CreateProcess句柄影响了
MsgWaitForMultipleObjects的句柄是H,它分明是GetFileThread.Handle和外部程序无关的
var
H:THandle;
begin
H:=GetFileThread.Handle;
GetFileThread.Terminate;
WaitForSingleObject(H,INFINITE);
ShowMessage('Finish');
end;尽管H是局部变量在此代码段优先级最高,没什么问题
为了防止意外,还是将它改为其它名字,如THTH,结果仍然相同WaitForSingleObject和MsgWaitForMultipleObjects均会受到CreateProcess的干扰
搞了保卫战最简的重现工程,这次彻底凌乱了,情况和上面不同了,这次是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.
按下按钮死锁是这样发生的:
线程等待同步到主线程更新Caption,此时你主线程处理按下按钮,主线程永久等待在WaitFor,而线程又永久等待同步到主线,导致死锁。
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判断线程的结束,应该如何处理呢?
非常感谢
这个参数解释
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。
“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.真是麻烦了,求指教
主线程在子线程结束后就退出。那个EXE并不需要退出。
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.
现在只好这样写了: MyThread1.Terminate;
while not MyThread1.Finished do
Application.ProcessMessages;非常感谢pathletboy的帮助
没必要这么低效死循环判断标志,直接重载TThread.Destroy,在Destroy中最后PostMessage通知主窗体
没必要这么低效死循环判断标志,直接重载TThread.Destroy,在Destroy中最后PostMessage通知主窗体判断子线程结束前后都有代码,无法独立出来,不方便接收PostMessage啊
没必要这么低效死循环判断标志,直接重载TThread.Destroy,在Destroy中最后PostMessage通知主窗体判断子线程结束前后都有代码,无法独立出来,不方便接收PostMessage啊死等后面的全丢消息处理就可以了。
没必要这么低效死循环判断标志,直接重载TThread.Destroy,在Destroy中最后PostMessage通知主窗体判断子线程结束前后都有代码,无法独立出来,不方便接收PostMessage啊死等后面的全丢消息处理就可以了。
好的,非常感谢