- 基本概念
- 什么是线程
- 为什么要多线程
- 重复任务希望同时进行(比如对于数组中的每个元素都进行相同且耗时的操作)
- 多个不同任务希望同时进行,互不干扰(比如有多个后台线程需要做轮询等操作)
- 什么是线程池
- 一组预先创建的线程,可以被重复使用来执行多个任务
- 异步编程默认使用线程池
- 什么是线程安全
- 线程安全
- 同步机制
- 原子操作
- 常用实现方式一
- 线程
- 线程池
- 异步编程
- 考虑一下自带方法
- 线程`Thread`
- 线程的创建
- 线程的终止
- 调用`Thread.Interrupt`方法,中断线程的执行
- 如果线程中包含一个`while(true)`循环
- 不能用`Abort`
- 线程的挂起与恢复
- 线程安全于同步机制
- 原子操作
- 锁与信号量
- 轻量型
- 不要自己造轮子
C# 多线程
基本概念
什么是线程
线程是能独立运行的最小单位,也是程序能够并发执行的一段指令序列
线程是进程的一部分,一个进程可以包含多个线程,这些线程共享进程的资源
进程有入口线程,可以创建更多的线程
为什么要多线程
重复任务希望同时进行(比如对于数组中的每个元素都进行相同且耗时的操作)
比如我处理一个数组的元素,每个都要耗时,如果我每处理一个再处理下一个,时间是n*m
的
但是我如果同时进行处理,时间是m
的
多个不同任务希望同时进行,互不干扰(比如有多个后台线程需要做轮询等操作)
比如我主线程有一个任务读取变换的数,然后我额外开一个后台线程去发送我读取的数
不然我读取变化的数进行发送,发送十分耗时,这将导致我读取不完全
什么是线程池
一组预先创建的线程,可以被重复使用来执行多个任务
一个线程的创建和销毁是十分耗时的,开销很大
当我们需要执行一个小任务时,直接找线程池要一个,做完再把线程还给他
避免频繁地创建和销毁线程,从而减少了线程创建和销毁的开销,提高了系统的性能和效率
异步编程默认使用线程池
通过异步编程可以更优雅的调用线程池,不需要我们自己去调用线程池的代码
什么是线程安全
线程安全
多个线程访问共享资源时,对共享资源的访问不会导致数据不一致或不可预期的结果
public class TODO {static int cnt = 0;const int total = 1000000;static void Main() {var thread1 = new Thread(foo);var thread2 = new Thread(foo);thread1.Start();thread2.Start();thread1.Join();thread2.Join();Console.WriteLine(cnt);}static void foo() {for(int i = 0; i < total; i++) {cnt++;}}
}
上述代码在两个进程同时访问一个数时,期待结果是total*2
,但由于奇奇怪怪的原因,将会小于total
比如线程1
和线程2
同时拿到cnt
,同时对cnt++
,这将导致其中一个线程的++
是被覆盖的
(汇编解释为,我取出cnt
,自增然后赋值,他们同时赋值将导致本来自增两次的值只有一次生效)
简言之,同时发生导致少加一次
可以使用lock
解决此问题
同步机制
用于协调和控制多个线程之间执行顺序和互斥访问共享资源
确保线程按照特定的顺序执行,避免竞态条件和数据不一致的问题
原子操作
在执行过程中不会被中断的操作。不可分割,要么完全执行,要么完全不执行,没有中间状态
在多线程环境下,原子操作能够保证数据的一致性和可靠性,避免出现竞态条件和数据竞争的问题
只需要一步就能完成的操作,不是指一行代码,而是对于底层来说,汇编啥的,也是一步就能完成
当然也可以用提供的函数来InterLocked.foo(ref elem)
来实现原子操作
常用实现方式一
线程
new
一个 thread
线程池
使用thread pool
这个类型里面的方法
异步编程
async
和 await
asynchronization n.异步化 异步,非同步化
考虑一下自带方法
`Parallel` For、ForEach、Invoke
`PLINQ` AsParallel、AsSequential、AsOrdered
即不需要那么底层的去实现
线程Thread
线程的创建
创建Thread
实例,并传入ThreadStart
委托
还可以配置线程,如是否为后台线程
调用Thread.Start
方法,还可以传参
线程的终止
调用Thread.Join
方法,等待线程的结束
意味着谁Join
,我就要等谁结束了再继续别的事
简言之,用于等待一个线程结束
public class TODO {static void Main() {var thread = new Thread((x) => {Console.WriteLine("Hello, {0}", x);for (int i = 0; i < 10; i++) {Thread.Sleep(500);Console.WriteLine("i = {0}", i);}Console.WriteLine("finished!");});Console.WriteLine("start");thread.Start("Bob");thread.Join();Console.WriteLine("over");}
}
调用Thread.Interrupt
方法,中断线程的执行
会在相应线程中抛出ThreadInterruptedException
如果线程中包含一个while(true)
循环
那么需要保证包含等待方法,如IO操作
,Thread.Sleep
等
如果没有这些,那while(true)
会没有空来抛出异常
就小小的阻塞他一下 thread.Sleep(0);
不能用Abort
他会直接干掉这个线程,g
使用Abort
方法来强制终止线程可能导致一些严重的问题,包括资源泄漏和不可预测的行为
较新版本的.NET
中如果使用这个方法,会报PlatformNotSupportedException
推荐使用Thread.Interrupt
或CancellationToken
线程的挂起与恢复
Thread. Suspend
以及Thread.Resume
你的挂起可能让线程暂停在任意一种状态,这就见鬼了
较新版本的.NET
中,这两个方法已经被标记为Obsolete
,且调用会报错
推荐使用锁、信号量等方式实现这一逻辑
线程安全于同步机制
原子操作
interlocked