【问题标题】:Self Suspending a thread in Delphi when it's not needed and safely resuming在不需要时在 Delphi 中自动挂起线程并安全地恢复
【发布时间】:2011-05-23 00:26:20
【问题描述】:

这个问题涉及 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。

【问题讨论】:

  • 我不是专家,但我会简单地使用 Sleep(250) 或 SignalObjectAndWait(参见 msdn)来进行更精确的控制。

标签: multithreading delphi resume suspend


【解决方案1】:

有一个库允许在Delphi using condition variables 中实现生产者-消费者队列。这个场景实际上就是讨论的例子。

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

【讨论】:

    【解决方案2】:

    与其暂停线程,不如让它休眠。让它阻塞在某个可等待的句柄上,当句柄发出信号时,线程将被唤醒。

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

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

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

    【讨论】:

    • 我明白你的意思是使用 Windows 来支持队列。我们一直在使用很多现有代码,现在已经足够好了。但我肯定会在未来的工作中研究它,我确实喜欢它的自动性质。谢谢。
    【解决方案3】:

    我推荐 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;
    

    【讨论】:

    • 他提到他在 Delphi XE 上,所以他已经可以访问 Generics.Collections 单元中的 TThreadedQueue 泛型类。 TThreadedQueue 是一个 FIFO 队列,具有在 push 和 pop 时同步线程的能力。
    • 我们的库中仍然支持很多 Delphi 7 代码。但我会看看 TThreadedQueue 以供将来尝试。我喜欢给定的解决方案,因为它可以轻松更新我们现有的代码。
    • 这看起来是一个不错的解决方案,但是您打算什么时候调用 MyResume?队列会这样做吗?什么时候?它如何知道要唤醒哪些线程?或者您是否会在队列后面添加项目后立即唤醒所有睡眠线程。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-05
    • 1970-01-01
    • 1970-01-01
    • 2012-05-20
    • 2011-07-09
    相关资源
    最近更新 更多