在多线程工作队列处理器中管理ThreadPool饥饿?

时间:2020-03-06 14:38:49  来源:igfitidea点击:

我正在研究工作队列处理器的设计,其中QueueProcessor从队列中检索命令模式对象并在新线程中执行它。

我正在设法解决潜在的队列锁定情况,在这种情况下,嵌套命令可能会导致死锁。

例如。

将FooCommand对象放在队列上,然后QueueProcessor在其自己的线程中执行该队列。

执行中的FooCommand将BarCommand放置到队列中。

假设允许的最大线程数仅为1个线程,则QueueProcessor将处于死锁状态,因为FooCommand无限期地等待BarCommand完成。

如何处理这种情况?队列对象是工作的正确对象吗?是否有任何制衡手段可以解决此问题?

非常感谢。 (应用程序使用C.NET 3.0)

解决方案

对于像这样的简单情况,可以按需剥离更多线程的添加监视线程很有帮助。

基本上,每N秒检查一次是否已完成任何作业,如果尚未完成,则添加另一个线程。

这不一定解决更复杂的死锁问题,但可以解决这一问题。

对于较重的问题,我的建议是将等待时间限制为新产生的进程,换句话说,我们只能等待启动的内容,这样就永远不会陷入僵局,因为在那种情况下不可能进行循环。

我们可以重新设计东西,以便FooCommand不使用队列来运行BarCommand而是直接运行它,或者我们可以将FooCommand分成两个,并在使BarCommand排队后立即使前半部分停止,然后让BarCommand在FooCommand之后排队它已经完成了工作。

排队隐式地假设一个异步执行模型。通过等待命令退出,我们正在同步工作。

也许我们可以将命令分为三个部分:FooCommand1,直到必须发送BarCommand为止; BarCommand,最后是FooCommand2,在BarCommand完成之后继续执行。这三个命令可以分别排队。当然,BarCommand应该确保FooCommand2已排队。

如果我们自己构建队列对象,则可以尝试以下操作:

  • 动态添加新的服务线程。如果可用线程计数太长时间为零,请使用计时器并添加线程。
  • 如果一个命令试图将另一个命令排队,并等待结果,那么我们应该在同一线程中同步执行第二个命令。如果第一个线程仅等待第二个线程,则无论如何都不会获得并发收益。

我假设我们要对BarCommand排队,以便它能够与FooCommand并行运行,但是BarCommand将在以后需要结果。如果是这种情况,那么我建议我们使用Parallel Extensions库中的Future。

Bart DeSmet对此有很好的博客条目。基本上,我们想执行以下操作:

public void FooCommand()
{
    Future<int> BarFuture = new Future<int>( () => BarCommand() );

    // Do Foo's Processing - Bar will (may) be running in parallel

    int barResult = BarFuture.Value;

    // More processing that needs barResult
}

对于诸如Parallel Extensions之类的库,我会避免"自己动手"调度。