用于执行具有不同优先级的任意任务的线程池

时间:2008-09-01 21:57:16

标签: c++ windows multithreading

我正在努力想出一个针对我的工作有很多设计要求的线程池设计。这对于工作软件来说是一个真正的问题,这是一项艰巨的任务。我有一个有效的实现,但我想把它扔到SO,看看人们可以提出什么有趣的想法,以便我可以比较我的实现,看看它是如何叠加的。我试图尽可能地满足要求。

线程池需要执行一系列任务。任务可以是短时间运行(<1秒)或长时间运行(数小时或数天)。每个任务都有一个相关的优先级(从1 =非常低到5 =非常高)。任务可以在其他任务运行时随时到达,因此当它们到达时,线程池需要选择这些并在线程可用时安排它们。

任务优先级完全独立于任务长度。实际上,如果不运行任务就无法判断任务可以运行多长时间。

某些任务受CPU限制,而某些任务受IO限制。事先不可能知道给定的任务是什么(虽然我猜在任务运行时可能会检测出来)。

线程池的主要目标是最大化吞吐量。线程池应该有效地使用计算机的资源。理想情况下,对于CPU绑定任务,活动线程的数量将等于CPU的数量。对于IO绑定任务,应分配比CPU更多的线程,以便阻塞不会过度影响吞吐量。最大限度地减少锁的使用和使用线程安全/快速容器非常重要。

通常,您应该运行具有更高CPU优先级的更高优先级任务(ref:SetThreadPriority)。较低优先级的任务不应“阻止”更高优先级的任务运行,因此如果在所有低优先级任务运行时出现更高优先级的任务,则优先级较高的任务将运行。

任务具有与其关联的“最大运行任务”参数。每种类型的任务只允许一次最多运行该任务的许多并发实例。例如,我们可能在队列中有以下任务:

  • A - 1000个实例 - 低优先级 - 最大任务1
  • B - 1000个实例 - 低优先级 - 最大任务1
  • C - 1000个实例 - 低优先级 - 最大任务1

工作实施只能同时运行(最多)1 A,1 B和1 C.

它需要在Windows XP,Server 2003,Vista和Server 2008(最新的服务包)上运行。


作为参考,我们可能会使用以下界面:

namespace ThreadPool
{
    class Task
    {
    public:
        Task();     
        void run();
    };

    class ThreadPool
    {    
    public:
        ThreadPool();
        ~ThreadPool();

        void run(Task *inst);
        void stop();
    };
}

5 个答案:

答案 0 :(得分:5)

那么我们要选择什么作为这个的基本构建块。 Windows有两个看起来很有前途的构建块: - I / O完成端口(IOCP)和异步过程调用(APC)。这两个都为我们提供了FIFO排队,而不必执行显式锁定,并且在调度程序等位置具有一定量的内置OS支持(例如,IOCP可以避免某些上下文切换)。

APC可能稍微适合一些,但我们必须对它们略微小心,因为它们并不十分“透明”。如果工作项执行可警告的等待(:: SleepEx,:: WaitForXxxObjectEx等)并且我们意外地将APC分派给线程,则新分派的APC将接管该线程,暂停先前执行的APC,直到新的APC为止。完了。这对我们的并发要求不利,并且可能使堆栈溢出更有可能。

答案 1 :(得分:1)

  

它需要在Windows XP,Server 2003,Vista和Server 2008(最新的服务包)上运行。

系统的内置线程池的哪些功能使它们不适合您的任务?如果你想要定位XP和2003,你不能使用新的闪亮的Vista / 2008池,但你仍然可以使用QueueUserWorkItem和朋友。

答案 2 :(得分:0)

@DrPizza - 这是一个非常好的问题,是一个直接解决问题核心的问题。 QueueUserWorkItem和Windows NT线程池被排除的原因有几个(虽然Vista看起来很有趣,可能在几年内)。

首先,我们想要更好地控制它何时启动并停止线程。我们听说NT线程池如果认为任务很短,就不愿意启动新线程。我们可以使用WT_EXECUTELONGFUNCTION,但我们真的不知道任务是长还是短

其次,如果线程池已经填满了长时间运行的低优先级任务,那么高优先级任务就不可能及时运行。 NT线程池没有任务优先级的真实概念,所以我们不能做QueueUserWorkItem并说“哦顺便说一下,立即运行这个”。

第三,(根据MSDN)NT线程池与STA公寓模型不兼容。我不太确定这意味着什么,但我们所有的工作线程都在STA中运行。

答案 3 :(得分:0)

  

@DrPizza - 这是一个非常好的问题,是一个直接解决问题核心的问题。 QueueUserWorkItem和Windows NT线程池被排除的原因有几个(虽然Vista看起来很有趣,可能在几年内)。

是的,它似乎在Vista中得到了相当强化,现在非常多才多艺。

好的,我仍然有点不清楚你希望优先事项如何发挥作用。如果池当前正在运行类型为A且最大并发度为1且低优先级的任务,并且它也会获得一个类型为A(和最大并发1)的新任务,但这次具有高优先级,它应该怎么做?

暂停当前正在执行的A是多毛的(它可以保存新任务需要执行的锁定,使系统死锁)。它不能产生第二个线程,只是让它一起运行(允许的并发性只有1)。但它不能等到低优先级任务完成,因为运行时是无限制的,这样做会允许低优先级任务阻止高优先级任务。

我的推测是你追求的是后者的行为?

答案 4 :(得分:0)

@DrPizza:

  好的,我还有点不清楚   你希望优先事项奏效。如果   池正在运行任务   类型A的最大并发性   1和低优先级,它得到了   类型A的新任务(和最大值   并发1),但这次用的是   高优先级,应该怎么做?

这个有点棘手,虽然在这种情况下我认为我会很乐意简单地允许低优先级任务运行完成。通常,我们不会看到许多具有不同线程优先级的相同类型的任务。在我们的模型中,实际上可以安全地停止并稍后在某些明确定义的点上重新启动任务(原因不同于此),尽管这可能带来的并发症可能不值得冒险。

通常,只有不同类型的任务才会有不同的优先级。例如:

  • 任务 - 1000个实例 - 低优先级
  • B任务 - 1000个实例 - 高优先级

假设A任务已经出现并且正在运行,那么B任务已经到来,我们希望B任务能够或多或少地立即运行。