我是多线程的新手,但不是一个完整的新手。我需要在工作线程中执行对Web服务的调用。
在主线程中,我有一个表单(TForm),它有一个私有数据成员(私有字符串),只有工作线程才会写入(我会在它恢复之前将指针传递给线程)。当工作线程完成其webservice调用并将结果响应xml写入表单上的私有成员时,工作线程使用PostMessage向表单的句柄发送消息(我在恢复之前也将其传递给线程)。 / p>
interface
const WM_WEBSERVCALL_COMPLETE = WM_USER + 1;
type
TWebServiceResponseXML = string;
PWebServiceResponseXML = ^TWebServiceResponseXML;
TMyForm = class(TForm)
...
private
...
fWorkerThreadID: Cardinal;
fWebServiceResponseXML: TWebServiceResponseXML;
public
...
procedure StartWorkerThread;
procedure OnWebServiceCallComplete(var Message: TMessage); Message WM_WEBSERVCALL_COMPLETE;
end;
TMyThread = class(TThread)
private
protected
procedure Execute; override;
public
SenderHandle: HWnd;
RequestXML: string;
ResponseXML: string;
IMyService: IService;
PResponseXML: PWebServiceResponseXML;
end;
implementation
procedure TMyForm.StartWorkerThread;
var
MyWorkerThread: TMyThread;
begin
MyWorkerThread := TMyThread.Create(True);
MyWorkerThread.FreeOnTerminate := True;
MyWorkerThread.SenderHandle := self.Handle;
MyWorkerThread.RequestXML := ComposeRequestXML;
MyWorkerThread.PResponseXML := ^fWebServiceResponseXML;
MyWorkerThread.Resume;
end;
procedure TMyForm.OnWebServiceCallComplete(var Message: TMessage);
begin
// Do what you want with the response xml string in fWebServiceResponseXML
end;
procedure TMyThread.Execute;
begin
inherited;
CoInitialize(nil);
try
IMyService := IService.GetMyService(URI);
ResponseXML := IMyService.Search(RequestXML);
PResponseXML := ResponseXML;
PostMessage(SenderHandle, WM_WEBSERVCALL_COMPLETE, 0, 0);
finally
CoUninitialize;
end;
end;
效果很好,但现在我想从数据模块(没有句柄)做同样的事情 ...所以我真的很感激一些有用的代码来补充工作模特我。
修改
我真正想要的是允许我替换行
的代码(如果可能的话)MyWorkerThread.SenderHandle := self.Handle;
与
MyWorkerThread.SenderHandle := GetHandleForThisSOAPDataModule;
答案 0 :(得分:9)
我之前使用过这种技术取得了一些成功:Sending messages to non-windowed applications
基本上,在通过AllocateHWND获得的句柄上使用第二个线程作为消息泵。这无疑是令人恼火的,你最好使用库来处理所有细节。我更喜欢OmniThreadLibrary,但还有其他人 - 请参阅How Do I Choose Between the Various Ways to do Threading in Delphi?和Delphi - Threading frameworks。
答案 1 :(得分:5)
您可以使用AllocateHwnd分配自己的句柄,并将其用作PostMessage目标。
TTestThread = class(TThread)
private
FSignalShutdown: boolean;
// hidden window handle
FWinHandle: HWND;
protected
procedure Execute; override;
// our window procedure
procedure WndProc(var msg: TMessage);
public
constructor Create;
destructor Destroy; override;
procedure PrintMsg;
end;
constructor TTestThread.Create;
begin
FSignalShutdown := False;
// create the hidden window, store it's
// handle and change the default window
// procedure provided by Windows with our
// window procedure
FWinHandle := AllocateHWND(WndProc);
inherited Create(False);
end;
destructor TTestThread.Destroy;
begin
// destroy the hidden window and free up memory
DeallocateHWnd(FWinHandle);
inherited;
end;
procedure TTestThread.WndProc(var msg: TMessage);
begin
if Msg.Msg = WM_SHUTDOWN_THREADS then
// if the message id is WM_SHUTDOWN_THREADS
// do our own processing
FSignalShutdown := True
else
// for all other messages call
// the default window procedure
Msg.Result := DefWindowProc(FWinHandle, Msg.Msg,
Msg.wParam, Msg.lParam);
end;
您可以将此应用于任何不仅仅是线程。请注意,如here所示,AllocateHWND不是线程安全的。
答案 2 :(得分:3)
基于事件使用的替代方案:
将线程(已存在)的OnTerminate与标志结合使用:
TMyDataModule = class(TDataModule)
private
procedure OnWebServiceCallComplete(Sender: TObject);
...
TMyThread = class(TThread)
public
property TerminateFlag: Integer ...
...
procedure TMyDataModule.StartWorkerThread;
...
MyWorkerThread.OnTerminate := <Self.>OnWebServiceCallComplete;
...
procedure TMyDataModule.OnWebServiceCallComplete(Sender: TObject);
begin
if MyWorkerThread.TerminateFlag = WEBCALL_COMPLETE then
...
end;
在Execute例程中设置TerminateFlag。即使FreeOnTerminate为True,OnTerminate也会自动触发。
向线程类添加一个新的事件属性,您可以在该线程类中将标志作为参数来指示终止/线程结果。像shown here这样的东西。务必同步事件调用。或者忘记参数,只是在执行正常完成时调用事件(就像你现在正在做的那样)。