线程
概念:
- 线程是-一个可执行路径,它可以独立于其它线程执行。
- 每个线程都在操作系统的进程(Process) 内执行,而操作系统进程提供了程序运行的独立环境。
- 单线程应用,在进程的独立环境里只跑一个线程,所以该线程拥有独占权。
- 多线程应用,单个进程中会跑多个线程,它们会共享当前的执行环境(尤其是内存)
- 例如一个线程在后台读取数据,另一个线程在数据到达后进行展示。
- 这个数据就被称作是共享的状态。
线程抢占:
当线程在执行于另一个线程上代码的执行交织的那一点时,就可以称之为线程抢占。
线程常用属性:
- 线程-旦开始执行,IsAlive 就是true,线程结束就变成false。
- 线程结束的条件就是:线程构造函数传入的委托结束了执行。
- 线程一旦结束,就无法再重启。
- 每个线程都有个Name属性,通常用于调试。
- 线程Name只能设置-次,以后更改会抛出异常。
- 静态的Thread.CurrentThread属性,会返回当先执行的线程
Thread.Join() 和 Thread.Sleep()
- join() 方法可以等待另一个线程执行结束才继续往下执行。可以传入一个以毫秒为单位的数值,或者TimeSpan 都可以。
- 如果join() 方法返回true,则表示线程结束了;如果超时了,就返回false。
- Thread.Sleep() 方法会暂停当前的线程,并且等待一段时间。
注意:
- Thread.Sleep(0) 这样的调用会导致线程立即放弃本身当前的时间片,自动将CPU移交给其他线程。
- Thread.Yield() 做同样的事情,但是它只会把执行交给同意处理器上的其他线程。
- 当等待Sleep 或 Join 的时候,线程处于阻塞的状态。
阻塞
- 当线程的执行由于某种原因导致暂停,那么就认为该线程被阻塞了。例如在Sleep() 或者 通过Join() 等待其他线程结束。
- 被阻塞的线程会立即将其处理器的时间片生成给其他线程,从此就不再消耗处理其时间,直至满足阻塞条件时。
- 可以通过ThreadState 这个属性来判断线程是否处于被阻塞的状态。
ThreadState 线程状态
ThreadState 是一个flags enum,通过按位的形式,可以合并数据的选项。
但它大部分的枚举值都没什么用,一般而言,最常用的是四个值,Unstarted,Running,WaitSleepJoin 和 Stopped。
代码语言:javascript复制bool blocked = (someThread. ThreadState & ThreadState .WaitSleepJoin) != 0;
可以通过以上的代码来判断线程是否是运行状态。
解除阻塞
当遇到下列四种情况的时候,就会解除阻塞。
- 阻塞条件被满足
- 操作超时(如果设置超时的话)
- 通过Thread.Interrupt()进行打断
- 通过Thread.Abort()进行中止
上下文切换
当线程阻塞或解除阻塞时,操作系统将执行上下文切换。这会产生少量开销,通常为1或2微秒。
阻塞 和 忙等待
线程各个状态示意图:
补充:
I/O 密集 和 计算密集
数据状态——本地和共享
对于共享状态的数据,保持线程安全则是非常重要的,因此
线程安全
为了维持线程安全,我们需要使用锁
在保证了线程安全的情况下,我们需要向线程传递数据
线程传递数据
补充一点在C#3.0之前
需要注意的是,使用lambda表达式时,需要注意变量的存放。
异常处理
一般情况下,如果需要捕获子线程执行时的出现的异常,则需要在子线程执行的代码中编写try/catch块来捕获异常,主线程中的try/catch不会对子线程中的异常起作用。
前后台线程
一般情况下,创建的线程属于两类,即前后台线程
前后台线程退出:
线程优先级
线程存在优先级,优先级越高的线程越先被分配到时间片,即越先执行。 在需要的情况下,我们可能需要提升一些线程的优先级,以便应用程序正常运行。
信号
在一些情境下,我们需要线程按照我们的指示进行运行等待,这就需要信号。
多客户端应用程序
对于一些多客户端的应用,程序需要保持UI响应及时,同时又要处理计算等密集工作,这样的程序就需要多线程处理
同步上下文
通过同步上下文类可以进行子线程向主UI线程传递数据操作。