以下内容转载自http://blog.csdn.net/u010425776/article/details/54233279
上下文切换会带来额外的开销
线程的运行机制
- 一个CPU每个时刻只能执行一条线程;
- 操作系统给每条线程分配不同长度的时间片;
- 操作系统会从一堆线程中随机选取一条来执行;
- 每条线程用完自己的时间片后,即使任务还没完成,操作系统也会剥夺它的执行权,让另一条线程执行
什么是“上下文切换”?
当一条线程的时间片用完后,操作系统会暂停该线程,并保存该线程相应的信息,然后再随机选择一条新线程去执行,这个过程就称为“线程的上下文切换"
上下文切换的过程
- 暂停正在执行的线程;
- 保存该线程的相关信息(如:执行到哪一行、程序计算的中间结果等)
- 从就绪队列中随机选一条线程;
- 读取该线程的上下文信息,继续执行
上下文切换是有开销的
每次进行上下文切换时都需要保存当前线程的执行状态,并加载新线程先前的状态。
如果上下文切换频繁,CPU花在上下文切换上的时间占比就会上升,而真正处理任务的时间占比就会下降。
因此,为了提高并发程序的执行效率,让CPU把时间花在刀刃上,我们需要减少上下文切换的次数。
如何减少上下文切换?
-
减少线程的数量
由于一个CPU每个时刻只能执行一条线程,而傲娇的我们又想让程序并发执行,操作系统只好不断地进行上下文切换来使我们从感官上觉得程序是并发执的行。因此,我们只要减少线程的数量,就能减少上下文切换的次数。
然而如果线程数量已经少于CPU核数,每个CPU执行一条线程,照理来说CPU不需要进行上下文切换了,但事实并非如此。 -
控制同一把锁上的线程数量
如果多条线程共用同一把锁,那么当一条线程获得锁后,其他线程就会被阻塞;当该线程释放锁后,操作系统会从被阻塞的线程中选一条执行,从而又会出现上下文切换。
因此,减少同一把锁上的线程数量也能减少上下文切换的次数。 -
采用无锁并发编程
我们知道,如果减少同一把锁上线程的数量就能减少上下文切换的次数,那么如果不用锁,是否就能避免因竞争锁而产生的上下文切换呢?
答案是肯定的!但你需要根据以下两种情况挑选不同的策略:- 需要并发执行的任务是无状态的:HASH分段
所谓无状态是指并发执行的任务没有共享变量,他们都独立执行。对于这种类型的任务可以按照ID进行HASH分段,每段用一条线程去执行。 - 需要并发执行的任务是有状态的:CAS算法
如果任务需要修改共享变量,那么必须要控制线程的执行顺序,否则会出现安全性问题。你可以给任务加锁,保证任务的原子性与可见性,但这会引起阻塞,从而发生上下文切换;为了避免上下文切换,你可以使用CAS算法, 仅在线程内部需要更新共享变量时使用CAS算法来更新,这种方式不会阻塞线程,并保证更新过程的安全性。
- 需要并发执行的任务是无状态的:HASH分段