我需要在Delphi中创建一个具有以下特征的线程:
我无法向线程发送消息,因为它没有窗口句柄。
我应该使用WaitForObject的某些变体吗?如果是这样,等待的是什么?如果没有,那么如何让线程等待,然后在新数据进入队列时唤醒它?
我读过Multithreading - The Delphi Way,似乎没有回答我的问题。也许OmniThreadLibrary可以做我需要的事情;我无法分辨,因为文档很少。我不太了解一般的线程,以确定库是否会在这里提供帮助以及如何使用它(甚至为什么要使用它而不是仅使用TThread后代)。
答案 0 :(得分:13)
OmniThreadLibrary绝对可以帮到你。来自OTL发行版的测试5应该可以帮助您开始。
在此演示中,“开始”按钮创建线程并设置一些参数和计时器(如果不需要,可以在代码中删除)。 “更改消息”向线程发送消息,并在线程的OMChangeMessage方法中处理此消息。然后线程将一些信息发送回客户端(本演示中的OMSendMessage,但您可以在您正在进行工作的同一消息中执行此操作),主线程通过OmniEventMonitor组件接收此消息。 “停止”按钮停止工作线程。
如果在线程繁忙时有更多邮件到达,则只要您的工作方法完成其工作,它们就会排队并进行处理。当无事可做时,线程将在此过程中使用零CPU周期等待下一条消息。
修改
在Delphi 2009及更高版本中,Background Worker模式提供了一种更简单的解决方案。
答案 1 :(得分:2)
WaitForSingleObject()可以等待几种类型的同步对象。您可以使用Windows“事件”同步对象(与Delphi事件无关)。您创建事件(在SyncObjs,IIRC中有一个Delphi TEvent包装器),并调用WaitForSingleObject以等待该事件发出信号。当您必须唤醒线程时,您调用SetEvent将事件置于信号状态并返回WaitForSingleObject。您可以让一个线程使用WaitForMultipleObjects()等待多个对象中的一个(或全部) - 它还会告诉您哪个对象已发出信号。
答案 2 :(得分:1)
你绝对可以向线程发送消息,即使它没有窗口句柄。只需使用PostThreadMessage()
代替SendMessage()
或PostMessage()
。如果您在[delphi]标签中搜索PostThreadMessage()
,那么StackOverflow上会有更多信息 - 我不认为在这里复制所有内容是个好主意。
但是如果你不了解线程编程,那么从OTL开始而不是低级别的东西可能确实是一件好事。
答案 3 :(得分:1)
这是一个简单的例子,你可以做到这一点......
const
WM_MY_RESULT = WM_USER + $1;
type
TMyThread = class(TThread)
private
FKilled: Boolean;
FListLock: TRTLCriticalSection;
FList: TList;
FJobAdded: TEvent;
protected
procedure Execute; override;
procedure DoJob(AJob: Integer);
public
constructor Create(CreateSuspended: Boolean);
destructor Destroy; override;
procedure Kill;
procedure PushJob(AJob: Integer);
function JobCount: Integer;
function GetJob: Integer;
end;
TThreadingForm = class(TForm)
lstResults: TListBox;
se: TSpinEdit;
btn: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure btnClick(Sender: TObject);
private
FThread: TMyThread;
procedure OnMyResultMessage(var Msg: TMessage); message WM_MY_RESULT;
public
{ Public declarations }
end;
var
ThreadingForm: TThreadingForm;
implementation
{$R *.dfm}
{ TMyThread }
constructor TMyThread.Create(CreateSuspended: Boolean);
begin
FKilled := False;
InitializeCriticalSection(FListLock);
FList := TList.Create;
FJobAdded := TEvent.Create(nil, True, False, 'job.added');
inherited;
end;
destructor TMyThread.Destroy;
begin
FList.Free;
FJobAdded.Free;
DeleteCriticalSection(FListLock);
inherited;
end;
procedure TMyThread.DoJob(AJob: Integer);
var
res: Integer;
begin
res := AJob * AJob * AJob * AJob * AJob * AJob;
Sleep(1000); // so it would take some time
PostMessage(ThreadingForm.Handle, WM_MY_RESULT, res, 0);
end;
procedure TMyThread.Execute;
begin
inherited;
while not FKilled or not Self.Terminated do
begin
EnterCriticalSection(FListLock);
if JobCount > 0 then
begin
LeaveCriticalSection(FListLock);
DoJob(GetJob)
end
else
begin
FJobAdded.ResetEvent;
LeaveCriticalSection(FListLock);
FJobAdded.WaitFor(10000);
end;
end;
end;
function TMyThread.GetJob: Integer;
begin
EnterCriticalSection(FListLock);
try
Result := Integer(FList[0]);
FList.Delete(0);
finally
LeaveCriticalSection(FListLock);
end;
end;
function TMyThread.JobCount: Integer;
begin
EnterCriticalSection(FListLock);
Result := FList.Count;
LeaveCriticalSection(FListLock);
end;
procedure TMyThread.Kill;
begin
FKilled := True;
FJobAdded.SetEvent;
Terminate;
end;
procedure TMyThread.PushJob(AJob: Integer);
begin
EnterCriticalSection(FListLock);
try
FList.Add(Pointer(AJob));
FJobAdded.SetEvent;
finally
LeaveCriticalSection(FListLock);
end;
end;
{ TThreadingForm }
procedure TThreadingForm.OnMyResultMessage(var Msg: TMessage);
begin
lstResults.Items.Add(IntToStr(Msg.WParam));
end;
procedure TThreadingForm.FormCreate(Sender: TObject);
begin
FThread := TMyThread.Create(False);
end;
procedure TThreadingForm.FormDestroy(Sender: TObject);
begin
FThread.Kill;
FThread.WaitFor;
FThread.Free;
end;
procedure TThreadingForm.btnClick(Sender: TObject);
begin
FThread.PushJob(se.Value);
end;