反正知道TEvent的状态吗?

时间:2018-04-12 11:04:26

标签: delphi

我有几个主题,每个主题都有自己的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;

但它没有效率,因为我发信号通知所有线程而不是只发信号通知正在等待信号的第一个线程。我怎么能这样做?

3 个答案:

答案 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;