线程阻塞既是:
如果线程执行由于某种原因暂停,则可以认为线程阻塞。
我们常见的线程阻塞有:
hread.Sleep和Thread.Join方法
被阻塞的线程的基本操作原理:
被阻塞的线程会立刻将其处理器的时间片转移给其他线程,从此不再消耗处理器的时间,知道阻塞条件被满足。
判断线程是否被阻塞:
我们可以通过Thread.State来判断线程状态,值得一提的是,Thread.State是一个flag enum,可以通过二进制表示线程状态。
十进制数 | Thread.State | 备注 |
---|---|---|
0 | Running | 线程启动 |
1 | StopRequested | 请求线程停止 |
2 | SuspendRequested | 请求线程挂起 |
4 | Background | 后台线程执行转台 |
8 | Unstarted | 尚未对线程调用start |
16 | Stopped | 线程停止 |
32 | WaitSleepJoin | 线程已阻塞,可能调用了sleep或者join方法 |
64 | Suppended | 线程已挂起 |
128 | AbortRequested | 已经对线程调用了abort方法,但是线程未收到ThreadAbortException |
256 | Abort | 线程已死,但是未将状态修改为Stooped |
需要注意的是,Threadstate只适合诊断线程状态,但是不适用于线程同步,因为线程的状态可能在测试threadstate与对信息进行操作时候发生变化
阻塞对应的便是非阻塞(Unblocking)
非阻塞发生的条件:
1)阻塞条件被满足了
2)操作超时了
3)thread.interrupt打断了
4) thread.abort进行中止
除此自外,我们还需要了解上下文切换:
当线程阻塞或者解除阻塞,操作系统至少需要进行一次上下文切换
,并且产生适量的开销(1~2us)。
阻塞也有不同的类型:主要是IO-Bound和CPU-Bound
当线程花费大量时间用来等待某件事情发生,这便是IO-Bound,这里涉及了输入输出,Thread.sleep也是一种输入输出操作。比如说:
在线程上同步等待:console.readkey,thread.sleep,thread.join
异步操作:在委托完成后进行要等待一个回调函数。
当线程花费大量时间进行CPU密集操作,这便是CPU-Bound,这里有一个自旋需要了解:
while(datetime.now<nextstarttime);
spinning和blocking还是有细小的区别的:
在几微妙时间内条件满足的话可以使用短spinning,这样可以避免上下文开销与切换。
值得注意的是,阻塞并不是零成本,线程生存期间会占用1MB的内存,给CLR与操作系统带来开销。