阻塞
- 如果线程的执行由于某种原因导致暂停,那么就认为该线程被阻塞了。
- 例如在Sleep或者通过Join等待其他线程结束。
- 被阻塞的线程会立即将其处理器的时间片生成给其它线程,从此就不再消耗处理器时间,直到满足其阻塞条件为止。
- 可以通过ThreadState这个属性来判断线程是否处于被阻塞的状态:
bool`` blocked = (someThread.ThreadState&ThreadState.WaitSleepJoin)!=0
ThreadState
- ThreadState是一个flags enum,通过按位的形式,可以合并数据的选项。
但是它大部分枚举值都没什么用,下面的代码将ThreadState剥离为四个最有用的值之一:Unstarted、Runing、WaitSleepJoin和Stopped
//如果线程是下面几个状态就返回这个状态,否则就是Running状态
public static ThreadState SimpleThreadState(ThreadState ts)
{
return ts & (ThreadState.Unstarted | ThreadState.WaitSleepJoin | ThreadState.Stopped);
}
ThreadState属性可用于诊断的目的,但不适用于同步,因为线程状态可能会在测试ThreadState和对该信息进行操作之间发生变化。
解除阻塞Unblocking
- 当遇到下列四种情况的时候,就会解除阻塞:
- 阻塞条件被满足
- 操作超时(如果设置超时的话)
- 通过Thread.Interrupt()进行打断
- 通过Thread.Abort()进行中止
上下文切换
通过上面情况,可能会发生切换。
- 当线程阻塞或解除阻塞的时,操作系统将进行上下文切换。这会产生少量开销,通常为1或2微妙。
I/O-bound VS Compute-bound 或(CPU-Bound)
- 一个花费大部分时间等待某事发生的操作成为I/O-bound
- I/O绑定操作通常涉及输入或输出,但这不是硬性要求:Thread.Sleep()也被视为I/O-bound
- 相反,一个花费大部分时间执行CPU密集型工作的操作成为Compute-bound。
阻塞vs忙等待(自选) Blocking vs Spinning
- IO-bound操作的工作方式有两种:
- 在当前线程上同步的等待
- Console.ReadLine(),Thread.Sleep(),Thread.Join()…
- 异步的操作,在稍后操作完成时触发一个回调动作。
- 在当前线程上同步的等待
- 同步等待的I/O-bound操作将大部分时间花在阻塞线程上。
- 它们也可以周期性的在一个循环里进行“打转(自旋)” ```csharp // 示例 while(DateTime.Now < nextStartTime) Thread.Slepp(100);
while(DateTime.Now < nextStartTime); ```
- 在忙等待和阻塞方面有一些细微差别。
- 首先,如果您希望条件很快得到满足(可能在几微妙之内),则短暂自选可能会很有效,因为它避免了上下文切换的开销和延迟。
- .NET Framework提供了特殊的方法和类来提供帮助SpinLock和SpinWait。
- 其次,阻塞也不是零成本。这是因为每个线程在生存期间会占用大约1MB的内存,并会给CLR和操作系统带来持续的管理开销。
- 因此,在需要处理成百上千个并发操作的大量I/O-bound程序的上下文中,阻塞可能会很麻烦
- 所以,此类程序需要使用给基于回调的方法,在等待时完全撤销其线程。
- 首先,如果您希望条件很快得到满足(可能在几微妙之内),则短暂自选可能会很有效,因为它避免了上下文切换的开销和延迟。