如何从另一个线程安全地检查主线程标志?

时间:2017-05-26 12:46:35

标签: multithreading delphi delphi-2009

我在网上找到了这个代码并且它正在运行,但是我不确定是否可以从另一个线程直接读取主线程中的变量。在此示例中,标志(变量)是 CancelCopy 。 一般来说,我想知道如何从另一个线程中的主线程中读取变量的状态,但是立即无需等待。

dostuff

1 个答案:

答案 0 :(得分:2)

从技术上讲,您显示的代码很好,并且可以按预期工作。

然而,它有一个小错误。您将错误的指针值传递给pbCancel的{​​{1}}参数。但是,您的代码不会崩溃,因为您传递的指针实际上设置为CopyFileEx(),而nil将接受pbCancel指针,因此nil将忽略该参数

假设要做的是传递CopyFileEx()变量的地址,您可以随时将其设置为BOOL以取消复制。 TRUE将为您监视该变量,当设置变量时,您不需要从回调中手动返回CopyFileEx()(如果您的回调遇到与副本无关的错误,则返回PROGRESS_CANCEL本身,并且您希望因错误而中止副本)。不过,我不会使用全局变量。我会使用一个执行副本的Form的本地变量。

尝试更像这样的东西:

PROGRESS_CANCEL

说完了,还有其他需要注意的事项 - 你将type TFormMain = class(TForm) ... private CancelCopy: BOOL; // <-- BOOL, not Boolean ... end; ... type TCopyEx = record Source: String; Dest: String; Handle: HWND; PCancelCopy: PBOOL; end; PCopyEx = ^TCopyEx; const CFEX_CANCEL = WM_USER + 1; function CopyFileProgress(TotalFileSize, TotalBytesTransferred, StreamSize, StreamBytesTransferred: LARGE_INTEGER; dwStreamNumber, dwCallbackReason: DWORD; hSourceFile, hDestinationFile: THandle; lpData: Pointer): DWORD; stdcall; begin // no need to watch CancelCopy here... // do normal status handling here as needed... // use PCopyEx(lpData)^ as needed... end; function CopyExThread(p: PCopyEx): Integer; begin try if not CopyFileEx(PChar(p.Source), PChar(p.Dest), @CopyFileProgress, p, p.PCancelCopy, COPY_FILE_NO_BUFFERING) then begin if GetLastError() = ERROR_REQUEST_ABORTED then SendMessage(p.Handle, CFEX_CANCEL, 0, 0); end; finally Dispose(p); end; Result := 0; end; procedure TFormMain.ButtonCopyClick(Sender: TObject); var Params: PCopyEx; ThreadID: Cardinal; begin New(Params); Params.Source := EditOriginal.Text; Params.Dest := EditCopied.Text; Params.Handle := Handle; Params.PCancelCopy := @CancelCopy; // <-- pass address of CancelCopy here... CancelCopy := FALSE; CloseHandle(BeginThread(nil, 0, @CopyExThread, Params, 0, ThreadID)); end; procedure TFormMain.ButtonCancelClick(Sender: TObject); begin CancelCopy := TRUE; end; HWND属性传递给线程。如果TForm.Handle在线程仍在运行时由于任何原因(并且是,它可能发生)导致TForm被破坏/重新创建,则HWND值将指向无效窗口(或者更糟糕的是,重新使用旧的TCopyEx.Handle值的新窗口。)

通常,HWND属性不是线程安全的,因此仅仅因为这个原因,将TWinControl.Handle对象的HWND传递给一个不是一个好主意。工作线程,除非你能保证在线程运行时不会销毁TWinControl(在这个例子中,这是不可保证的)。

在这个例子中,我将使用一个不同的HWND,保证在线程的生命周期内是持久的,例如HWND窗口(发送到此窗口的消息可以通过{处理) {3}}),或调用TApplication.HookMainWindow()的结果。

例如:

TApplication.Handle