基于 QWorker 的多线程编程 – 附加选项

一般来说,我们不需要对 QWorker 中 Workers 这个包工头干涉太多,但如果你想干涉它的行为,QWorker 还是提供了一些额外的途径的。

1、控制工作者的数量

默认情况下, 包工头为了节省开支,会为雇佣的工人数量设置一个上限和下限。默认情况下,下限为2,上限为逻辑 CPU 核心数量×2 +1,也就是说,对于一个四核处理器,默认的工作者上限是2×4+1=9。在某些应用环境中,显然这过少了,所以,我们提供了两个属性来控制这些限制:

  • Workers.MinWorkers

这个属性用于控制工作者数量的下限,最小不能小于1(你总不能让包工头自己应付临时来的少量工作吧)。

  • Workers.MaxWorkers

这个属性用于控制工作者数量的上限,最大值受系统资源的限制。Delphi / C++ Builder 中,每个线程栈的开销最低是16KB,最大开销是1MB,而一个32位程序的用户地址空间实际上只有 2GB 左右,所以,理论上最坏的情况下上限可以达到2000左右,但实际上你线程中肯定还牵涉到资源的分配利用,最坏情况下恐怕达不到这个数值。而且过多的线程对系统的资源调度能力也是一个很大的考验,一般个人不推荐太多。

2、长时间作业

一般情况下,作业都是简单快速执行的函数。如果一个作业需要很长时间才能完成,那么由此从事这个作业的工人将被长期占用,无法执行其它作业,这样显然会影响到其它需要处理的作业。QWorker 提供了长时间作业(LongtimeJob)选项,用于执行这类任务。对于长时间作业数量,QWorker 提供了一个上限为所有工作者数量的一半,超过这个数量时,长时间作业将无法投递执行。

从实际情况来说,长时间作业只是一个逻辑设定,而非硬性要求。你当然可以直接将需要较长时间才能完成的作业当作普通的作业投递并执行,但通过这样一个逻辑限定,可以帮助你重新思考你的业务处理逻辑,从而提升设计的品质。

从事长时间作业的工作者数量受属性 MaxLongtimeWorkers 限制,它的上限是工作者总数量的一半。

3、优化批量添加作业过程

正常投递作业的时候,QWorker 每投递一个作业,都会尝试去检查有没有空闲的工人能够处理这一作业。如果没有,就雇佣新的工人来处理作业,以保证作业得到快速处理。但在批量添加作业的情况下,这样的检查开销可以通过调用 DisableWorkers 来禁止检查工人是否空闲及获取新作业来提高投递的效率。

DisableWorkers 内部维护着一个计数器,每调用一次 DisableWorkers 这个计数器都会加1,而与其配对的另一个函数 EnableWorkers 则负责将这个计数器减1,当这个计数器减为0时,就会恢复正常的过程并启用必要的空闲工人来处理投递的作业。

您也可以直接设置 Enabled 属性的值来间接调用 DisableWorkers 和 EnableWorkers,但要注意一点,设置 Enabled 为 True 时,由于它是调用 EnableWorkers 来减少计数,所以未必在之后读取 Enabled 的值就是 True。

4、工作者被解雇的时间设置

QWorker 中,一个工人如果没有任何作业需要它去做,那么会引发一个计时,在这个计时完成后,如果它仍没有得到任何机会执行作业,它就会被解雇,占用的资源会被释放。这个计时由属性 FireTimeout 来指定,单位为毫秒,默认值为15000,即15秒。

5、枚举工作者状态

如果我们的作业出现了死锁怎么办? QWorker 提供了枚举工作者状态函数 EnumWorkerStatus 来列举出每一个工作者的状态和调用堆栈信息,帮助你快速定位错误来源。相关的内容请参考文章:QWorker更新-在Win32平台上枚举作业状态加上堆栈显示

6、其它一些辅助属性信息

 

  • Terminating 属性用来标志当前是否处于结束状态中,处于此状态时,Workers 对象自身正在释放中,不会接受任何新作业。
  • Workers 属性用来显示当前雇佣的工作者数量
  • BusyWorkers 属性用于显示当前忙碌工作者数量
  • IdleWorkers 属性用于显示当前空闲工作者数量
  • OutOfWorker 属性用于标记是否已经到达最大工作者数量(BusyCount==MaxWorkers)
  • NextRepeatJobTime 属性用于标记定时等重复作业最近的一次触发时间
  • OnCustomFreeData 事件用于处理用户自定义类型的作业附加数据的内存释放问题,具体可以参考文章:自定义作业数据指针释放方法

7、一些辅助函数的简单说明

QWorker 提供了一些简单的函数来辅助完成一些工作,下面是它们的简单说明:

  • MakeJobProc :将一个全局函数转换为类的成员方法,这个主要是内部使用
  • GetCPUCount: 获取系统中CPU的核心数量,这个数量在XE6以后有一个 CPUCount 全局变量可以直接使用
  • GetTimestamp: 获取当前系统的时间戳,最高可精确到0.1ms,但实际受操作系统限制
  • SetThreadCPU:设置线程运行的CPU
  • AtomicAnd:原子位与操作
  • AtomicOr:原子位或操作
  • JobPoolCount:作业缓冲池中的已经缓冲的元素数量
  • JobPoolPrint:打印作业缓冲池中的对象信息

到此 QWorker 多线程编程的教程暂告一段落,如果有什么疑问,可以在群里或者在后面评论里咨询。在此,感谢大家的大力支持。

分享到: