7

这个问题涉及 Delphi 和 XE 专门弃用 Suspend 和 Resume。我已经阅读了其他帖子,但到目前为止我还没有找到类似的用法,所以我将继续要求讨论。

我想知道是否有更好的方法在不需要时暂停线程?

我们有一个多年来一直使用的 Delphi 类,它基本上是一个与线程进程相关联的 FIFO 队列。队列接受主线程上的数据对象,如果线程被挂起,它将恢复它。

作为线程执行过程的一部分,对象从队列中弹出并在线程上处理。通常这是进行数据库查找。

在进程结束时,对象的属性被更新并标记为可用于主线程或传递到另一个队列。Execute 过程的最后一步(实际上是第一步)是检查队列中是否还有其他项目。如果有它继续,否则它会暂停自己。

它们的关键是当它完成时唯一的挂起操作是在执行循环内,并且在正常操作期间的唯一恢复是在将新项目放入队列时调用的。例外情况是队列类被终止时。

resume 函数看起来像这样。

process TthrdQueue.MyResume();
  begin
    if Suspended then begin
      Sleep(1); //Allow thread to suspend if it is in the process of suspending
      Resume();
    end;
  end;

执行看起来与此类似

process TthrdQueue.Execute();
  var
    Obj : TMyObject;
  begin
    inherited;
    FreeOnTerminate := true;
    while not terminated do begin
      if not Queue.Empty then begin
        Obj :=  Pop();
        MyProcess(Obj);  //Do work
        Obj.Ready := true;
      end
      else
        Suspend();  // No more Work
    end;   //Queue clean up in Destructor
  end;  

TthrdQueue Push 例程在堆栈中添加另一个对象后调用 MyResume。MyResume 仅在线程挂起时调用 Resume。

关闭时,我们将 terminate 设置为 true,如果它被挂起,则调用 MyResume。

4

3 回答 3

7

我推荐 TthrdQueue 的以下实现:

type
  TthrdQueue = class(TThread)
  private
    FEvent: THandle;
  protected
    procedure Execute; override;
  public
    procedure MyResume;
  end;

implementation

procedure TthrdQueue.MyResume;
begin
  SetEvent(FEvent);
end;

procedure TthrdQueue.Execute;
begin
  FEvent:= CreateEvent(nil,
                       False,    // auto reset
                       False,    // initial state = not signaled
                       nil);
  FreeOnTerminate := true;
  try
    while not Terminated do begin
      if not Queue.Empty then begin
        Obj :=  Pop();
        MyProcess(Obj);  //Do work
        Obj.Ready := true;
      end
      else
        WaitForSingleObject(FEvent, INFINITE);  // No more Work
    end;
  finally
    CloseHandle(FEvent);
  end;
end;
于 2010-12-09T18:02:20.273 回答
3

与其挂起线程,不如让它休眠。使其阻塞在某个等待句柄上,当句柄发出信号时,线程将唤醒。

可等待对象有很多选项,包括事件、互斥对象、信号量、消息队列、管道。

假设您选择使用事件。使其成为自动重置事件。当队列为空时,调用事件的WaitFor方法。当其他东西填充队列或想要退出时,让它调用事件的SetEvent方法。

我首选的技术是使用操作系统消息队列。我会用消息替换您的队列对象。然后,编写一个标准GetMessage循环。当队列为空时,它会自动阻塞等待新消息。将终止请求变成另一条消息。(TThread.Terminate一旦你开始用线程做任何有趣的事情,这个方法就不是一个非常有用的功能,因为它不是虚拟的。)

于 2010-12-09T18:04:10.723 回答
3

有一个库允许使用条件变量在 Delphi 中实现生产者-消费者队列。这个场景实际上就是讨论的例子。

条件变量的经典例子是生产者/消费者问题。一个或多个称为生产者的线程生产项目并将它们添加到队列中。消费者(其他线程)通过从队列中删除生产的项目来消费项目。

于 2010-12-09T18:09:13.940 回答