线程(Thread)与进程(Process)二者都定义了某种边界,不同的是进程定义的是应用程序与应用程序之间的边界,不同的进程之间不能共享代码和数据空间,而线程定义的是代码执行堆栈和执行上下文的边界
一个进程可以包括若干个线程,同时创建多个线程来完成某项任务,便是多线程
当多个线程访问同一个全局变量,或者同一个资源(比如打印机)的时候,需要进行线程间的互斥操作来保证访问的安全性
CPS全称是Continuation Passing Style,在.NET中,它会自动编译为:
从状态机的角度出发,await的本质是调用Task.GetAwaiter()的UnsafeOnCompleted(Action)回调,并指定下一个状态号
从多线程的角度出发,如果await的Task需要在新的线程上执行,该状态机的MoveNext()方法会立即返回,此时,主线程被释放出来了,然后在UnsafeOnCompleted回调的action指定的线程上下文中继续MoveNext()和下一个状态的代码
而相比之下,GetResult()就是在当前线程上立即等待Task的完成,在Task完成前,当前线程不会释放
注意:Task也可能不一定在新的线程上执行,此时用GetResult()或者await就只有会不会创建状态机的区别了
Task和Thread都能创建用多线程的方式执行代码,但它们有较大的区别
Task较新,发布于.NET 4.5,能结合新的async/await代码模型写代码,它不止能创建新线程,还能使用线程池(默认)、单线程等方式编程,在UI编程领域,Task还能自动返回UI线程上下文,还提供了许多便利API以管理多个Task
发挥多核CPU的优势,防止阻塞
常用的如 SemaphoreSlim、ManualResetEventSlim、Monitor、ReadWriteLockSlim
lock是一个混合锁,其实质是 Monitor
lock的锁对象要求为一个引用类型,可以锁定值类型,但值类型会被装箱,每次装箱后的对象都不一样,会导致锁定无效
对于lock锁,锁定的这个对象参数才是关键,这个参数的同步索引块指针会指向一个真正的锁(同步块),这个锁(同步块)会被复用
多线程是实现异步的主要方式之一,异步并不等同于多线程
实现异步的方式还有很多,比如利用硬件的特性、使用进程或纤程等
在.NET中就有很多的异步编程支持,比如很多地方都有Begin、End 的方法,就是一种异步编程支持,内部有些是利用多线程,有些是利用硬件的特性来实现的异步编程
优点:减小线程创建和销毁的开销,可以复用线程,也从而减少了线程上下文切换的性能损失,在GC回收时,较少的线程更有利于GC的回收效率
缺点:线程池无法对一个线程有更多的精确的控制,如了解其运行状态等;不能设置线程的优先级;加入到线程池的任务(方法)不能有返回值;对于需要长期运行的任务就不适合线程池
Mutex是一个基于内核模式的互斥锁,支持锁的递归调用
Lock是一个混合锁,一般建议使用Lock更好,因为lock的性能更好