C#多线程和异步
什么是线程
线程是一个可执行的路径,他可以独立于其他线程执行。
每个线程在操作系统的进程内执行,而操作系统提供了程序的运行独立环境。
单线程指的是环境中跑了一个线程,所以该线程拥有独占权。
多线程应用,单个进程中会跑多线程,他会共享当前执行环境。
线程的一些属性
线程一旦开始执行,isAlive就是true,线程结束就是false;
线程结束的条件就是,线程构造函数传入委托结束了执行。
线程一旦结束局无法重启。
线程都有个name属性用来调试。
静态的thread.currrentThread属性,会返回当前执行的线程。
多线程目的是为了处理复杂、耗时的工作,设置不同的线程处理不同的工作是能提高效率的方法。
线程生命周期:起始(System.Threading.Thread类被创建),结束(线程被终止)。
生命周期中的状态:
1.创建但为使用:创建好了线程但没使用start调用。
2.就绪状态:当线程准备好运行等待CPU周期状态
3.不可运行状态:①调用sleep。②调用了wait。③I/O阻塞。
4.死亡状态:线程执行完毕关闭了。
主线程:C#在执行的时候第一个线程就是主线程,也是自动创建的。
public static void CallToChildThread() { try { Console.WriteLine("Child thread starts"); // 计数到 10 for (int counter = 0; counter <= 10; counter++) { Thread.Sleep(500); Console.WriteLine(counter); } Console.WriteLine("Child Thread Completed"); } catch (ThreadAbortException e) { Console.WriteLine("Thread Abort Exception"); } finally { Console.WriteLine("Couldn't catch the Thread Exception"); } } static void Main() { ThreadStart childref = new ThreadStart(CallToChildThread); Console.WriteLine("In Main: Creating the Child thread"); Thread childThread = new Thread(childref); childThread.Start(); // 停止主线程一段时间 Thread.Sleep(2000); // 现在中止子线程 Console.WriteLine("In Main: Aborting the Child thread"); childThread.Abort(); }
这里只是线程的体现,一般不会这样使用。
Join and sleep
调用join方法,就可以等待另一个线程的结束。
调用join的时候,可以设置一个超时,使用毫秒或者TimeSpan。
调用sleep就是等待
注意:Thread.Sleep(0)这样调用会导致线程放弃本身的时间片,自动将cpu移交给其他线程。
阻塞:如果线程的执行由于什么原因导致暂停,就是线程阻塞了。
被阻塞的线程会立即将处理器的时间片交个其他线程,从此就不在消耗处理器的时间,直到满足阻塞条件。
可以通过ThreadState这个竖向判断线程是否被阻塞
bool blocked=(someThread.ThreadState & ThreadState.waitSleepJion)!=0
ThreadState 属性的取值如下:
Aborted:线程已停止;
AbortRequested:线程的Thread.Abort()方法已被调用,但是线程还未停止;
Background:线程在后台执行,与属性Thread.IsBackground有关;
Running:线程正在正常运行;
Stopped:线程已经被停止;
StopRequested:线程正在被要求停止;
Suspended:线程已经被挂起(此状态下,可以通过调用Resume()方法重新运行);
SuspendRequested:线程正在要求被挂起,但是未来得及响应;
Unstarted:未调用Thread.Start()开始线程的运行;
WaitSleepJoin:线程因为调用了Wait(),Sleep()或Join()等方法处于封锁状态;
解除阻塞:
但遇到下面四种情况才解除阻塞
1.阻塞条件满足
2.操作超时
3.通过Thread.Interrupt()进行打断
4.通过Thread.Abort()进行中止
IO-bound和Compute-bound
一个话费大量时间去等待某件事发生叫做IO-bound
IO-bound绑定操作通常涉及输入输出,但不是硬性要求,Thread.Sleep()也被叫做IO-bound
IO-bound也可以是干等这,什么也不干
相反,话费大量时间执行cpu密集工作的叫做Compute-Bound
compute-bound就是指的很忙,没时间
阻塞和忙等待:Blocking and Spinning
IO-bound操作工作方式有两种:
在当前线程上同步等待:Console.ReadLine(),Thread.sleep(),
异步操作,稍后操作完等待回调
忙等待:while(DateTime.Now<nextStartTime);
忙等待和阻塞有些差别:
希望时间短,就考虑忙等待.net framework 提供了特殊的方法SpinLock和SpinWait
其次,阻塞线程成本比较高,会带来额外开销(上下文切换,会有2微秒)
因此,处理大量的IO-bound,阻塞很麻烦,所以需要基于回调,等待时期撤销线程。
本地VS共享状态
CLR为每个线程分配了自己的内存占(stack),以便本地变量保持独立
共享:
如果多个线程都引用同一个对象实例,那么就共享数据
被lambda表达式活匿名委托捕获的变量,也是公用的
静态字段