前一章我们说了,QWorker 是基于作业的视角来考虑的,所以,这一章,我们试图从不同的视角来解读下作业的分类,古语说“横看成岭侧成峰,远近高低各不同”,我们也横七竖八的切分下看看。
1、从作业的运行线程环境来分
我们知道,许多东西如窗体上的控件,都不是线程安全的,如果我们在后台线程中直接访问它,会发生意想不到的错误。所以,如果作业需要访问窗体上的东西,那么千万要记得将作业的 ARunInMainThread 参数设置为 True 。从这一点上分析,我们可以将作业分为主线程作业和后台线程作业。
- 主线程作业
主线程作业适合于做一些用户界面相关的交互操作,主要是因为用户界面上的各个控件,很少有是线程安全的。
- 后台线程作业
后台线程作业适合于做实际的数据获取及处理工作,在处理过程中,如果需要与界面交互,就可以直接投递一个主线程作业来处理。在后台线程作业中,我们许多时候,需要考虑多线程同步的问题,希望大家在设计程序时一定要注意这一点。
2、按作业的执行时间点划分
我们的作业投递给 Workers 这个包工头后,我们可能希望立即安排工人去完成作业,也可能希望过一会再去完成作业,甚至我们可能希望在我们给包工头一个眼神后,包工头再安排工人去完成作业。所以,从作业的执行时间点上,我们将作业分为立即执行、延迟执行和等待信号执行三种。
- 立即执行
我们将作业投递(Post)给 Workers 这个包工头后,它会尽量立即安排工人去执行作业(LookupIdleWorker)。注意一点,这个作业如果要求运行在主线程,而你的投递操作也在主线程,那么它必需等待主线程进入消息处理循环时才能得到处理。同时,如果没有空闲的工人可用,你的作业也会被放到队列里等待有空闲工人时才能被处理。
- 延迟执行
我们将作业投递(Delay/At)给 Workers 这个包工头,要求它延迟一段时间再去执行(比如说2秒后执行)。注意一点,QWorker 里,时间的最小单位是 0.1ms,所以,如果1秒对应的值是10000,您可以用Q1Second来代替。
- 等待信号
在看谍战电影时,我们许多时候,看见谍报人员在阳台上放盆花,一旦花盆没了,那就说明出事了,而这就是所谓的信号的一种。我们可以将作业投递给 Workers 这个包工头时,告诉它等待(Wait)我们的特定信号。一旦我们触发了相应的信号(花盆没了:)),包工头会立即安排工人去执行指定的作业。
因为信号的发出和执行的作业是完全分离的,所以,信号的触发不需要知道是否有人响应,而信号的响应者不需要知道啥时候自己该去执行,从而实现了业务逻辑的松散耦合。
3、按作业的执行次数分
一个作业,有可能只执行一次,有可能需要重复执行多次。从这个视角上来分,我们将作业分为单次作业和重复作业两种。顾名思义,单次作业在我们投递后,只会被调度执行一次,重复作业则相反,它将至少执行一次。
单次作业的触发,我们一般调用 Workers.Post、Workers.Delay、Workers.At 函数来触发,不过它的重复间隔参数(AInterval)被设置为0,如果我们在投递时,设置其重复间隔不为0,则作业会被重复的调度执行。
对于等待特定信号触发的作业(Workers.Wait),我们一般认为它是重复作业,只是重复的频率是不定的,取决于信号的触发次数。
4、从作业的触发方式分
一个作业啥时候被交付给工人执行,从这个视角,我们对作业来观察,会发现有三个不同的分类手工触发、定时触发和信号触发。
- 手工触发
这类作业一般是由程序员在需要时直接在投递(Post)一个后台作业给包工头,包工头会立即安排作业的执行。
- 定时触发
定时触发的作业,要求作业在特定的时间点运行,它一般由 Delay、At 函数投递给包工头,由包工头内部在到达指定的时间后,触发执行。
- 信号触发
就象前面说的,信号触发的作业是通过 Wait 函数投递给包工头的。而在另外的地方,通过 Signal 函数触发信号后,由包工头调度执行。
与前面两种方式相比,信号触发类型的作业步骤稍显多一些:
- 信号注册
调用 Workers.RegisterSignal 来注册一个信号,然后会返回一个用来唯一标记这个名称信号的ID。
- 作业投递
调用 Workers.Wait 函数告诉QWorker自己要等待的信号名称或ID,推荐用ID,开销更小一点点,但用信号当然也可以,只不过触发时,需要先遍历所有信号找到相应的信号ID,然后执行触发过程。
- 信号触发
在需要执行相关操作时,调用 Workers.Signal 函数触发信号,同样推荐使用ID。在上一步投递的信号响应作业,会自动被触发,至此完整的信号触发过程完成。
上面列出了作业自己从不同视角对作业的分析。诚然,你完全可以从另外的角度去分析作业的不同分类,欢迎进一步的补充和分析。