我有几个主题,每个主题都有自己的TEvent
:
TWorkerThread = class(TThread)
private
FSignal: TEvent;
public
constructor Create;
destructor Destroy; override;
procedure Execute; override;
end;
procedure TWorkerThread.Execute;
var ID: integer;
begin
while not Terminated do begin
Tmonitor.Enter(MainQueue);
try
if (MainQueue.Count > 0) then ID := MainQueue.dequeue
else ID := 0;
finally
Tmonitor.exit(MainQueue);
end;
if (ID <> 0) then ProcessID(ID)
else fSignal.WaitFor(INFINITE);
end;
end;
现在,在主线程中我想检索等待信号的线程列表,所以那些正在做的人fSignal.WaitFor(INFINITE);
其实我在代码中也喜欢这个:
procedure AddIDToTheQueue(const id: integer);
begin
Tmonitor.Enter(MainQueue);
try
MainQueue.Enqueue(id);
//signal all background thread !!! this a don't like i prefer to signal only one thread who are waiting instead of signaling all the threads !!!
for I := Low(workerThreads) to High(workerThreads) do
workerThreads[i].Signal.SetEvent;
finally
Tmonitor.Exit(MainQueue);
end;
end;
但它没有效率,因为我发信号通知所有线程而不是只发信号通知正在等待信号的第一个线程。我怎么能这样做?
答案 0 :(得分:4)
要直接回答您的问题,您可以随时使用以下方式检查事件是否已发出信号:
if FSignal.WaitFor(0) = wrSignaled then begin
// event is signaled
end;
如果您需要处理所有案件,当然:
case FSignal.WaitFor(0) of
wrSignaled:;
wrTimeout:;
wrAbandoned:;
wrError:;
wrIOCompletion:;
end;
但是 - 这是通过简单地使用任务来利用内置线程池的理想情况。
例如,您的所有代码都可以删除并简化为:
procedure AddIDToTheQueue(id: integer);
var
LTask : ITask;
begin
LTask := TTask.Create(procedure begin ProcessID(id); end);
LTask.Start;
end;
将integer
作为const
传递是没有意义的。无论您传递值还是指针,它的大小都相同。如果你没有修改它,它就没有区别。
答案 1 :(得分:2)
在这种情况下,我不会使用事件。
我会使用yield。
然后执行变为
private final SourceCache sourceCache;
private final ServiceCache serviceCache;
private final MethodCache methodCache;
private final ModelCache modelCache;
private final QueryFactory queryFactory;
public MetaDataPersistenceHandler(
final Transaction transaction)
{
super(transaction);
this.transaction = transaction;
this.sourceCache = new SourceCache(transaction);
this.serviceCache = new ServiceCache(transaction);
this.methodCache = new MethodCache(transaction);
this.modelCache = new ModelCache(transaction);
this.queryFactory = new QueryFactory();
this.transaction.addQueryFactory(this.queryFactory);
}
public MetaDataPersistenceHandler(
final Transaction transaction,
final long fileSize)
{
super(transaction, fileSize);
this.transaction = transaction;
this.sourceCache = new SourceCache(transaction);
this.serviceCache = new ServiceCache(transaction);
this.methodCache = new MethodCache(transaction);
this.modelCache = new ModelCache(transaction);
this.queryFactory = new QueryFactory();
this.transaction.addQueryFactory(this.queryFactory);
}
和AddIDToTheQueue变为
procedure TWorkerThread.Execute;
var
ID : integer;
begin
while not Terminated do begin
Tmonitor.Enter(MainQueue);
try
if (MainQueue.Count > 0) then ID := MainQueue.dequeue
else ID := 0;
finally
Tmonitor.exit(MainQueue);
end;
if (ID <> 0) then ProcessID(ID)
else Yield;
end;
end;
您没有尝试告诉您的主题有新ID可用。你让第一个可用的线程自动处理它。
答案 2 :(得分:1)
调用 TEvent.WaitFor(0)
有点慢...你可以创建一个新的漂亮的事件,它使用临界区来快速检查信号状态。在这个例子中,我还为事件添加了一个值,它也可以帮助您发送带有该信号的命令。
TSafeCardinalEvent = class
private
CardinalValue: Cardinal;
CS: TRTLCriticalSection;
hEvent: THandle;
function GetEventValue: Cardinal;
procedure SetEventValue(AValue: Cardinal);
public
constructor Create; virtual;
destructor Destroy; override;
function Signaled: Boolean; overload; {$IFDEF RELEASE} inline; {$ENDIF}
function Signaled(out Code: Cardinal): Boolean; overload;
property Value: Cardinal read GetEventValue write SetEventValue;
property EventHandle: THandle read hEvent;
end;
constructor TSafeCardinalEvent.Create;
begin
inherited Create;
CardinalValue:= 0;
InitializeCriticalSection(CS);
hEvent:= CreateEvent(nil, True, False, nil);
end;
destructor TSafeCardinalEvent.Destroy;
begin
CloseHandle(hEvent);
DeleteCriticalSection(CS);
inherited Destroy;
end;
function TSafeCardinalEvent.Signaled: Boolean;
begin
EnterCriticalSection(CS);
Result:= CardinalValue > 0;
LeaveCriticalSection(CS);
end;
function TSafeCardinalEvent.Signaled(out Code: Cardinal): Boolean;
begin
EnterCriticalSection(CS);
Code:= CardinalValue;
Result:= CardinalValue > 0;
LeaveCriticalSection(CS);
end;
function TSafeCardinalEvent.GetEventValue: Cardinal;
begin
EnterCriticalSection(CS);
Result:= CardinalValue;
LeaveCriticalSection(CS);
end;
procedure TSafeCardinalEvent.SetEventValue(AValue: Cardinal);
begin
EnterCriticalSection(CS);
CardinalValue:= AValue;
if CardinalValue = 0
then ResetEvent(hEvent)
else SetEvent(hEvent);
LeaveCriticalSection(CS);
end;