- 基本概念
- 线程`Thread`
- 线程安全于同步机制
-
- 原子操作
- 锁与信号量
-
-
- [lock & Monitor](#lock & Monitor)
- Mutex
- Semaphore
- WaitHandle
- ReaderWriterLock
-
- 轻量型
- 不要自己造轮子
基本概念
什么是线程
线程是能独立运行的最小单位,也是程序能够并发执行的一段指令序列
线程是进程的一部分,一个进程可以包含多个线程,这些线程共享进程的资源
进程有入口线程,可以创建更多的线程
为什么要多线程
重复任务希望同时进行(比如对于数组中的每个元素都进行相同且耗时的操作)
比如我处理一个数组的元素,每个都要耗时,如果我每处理一个再处理下一个,时间是n*m
的
但是我如果同时进行处理,时间是m
的
多个不同任务希望同时进行,互不干扰(比如有多个后台线程需要做轮询等操作)
比如我主线程有一个任务读取变换的数,然后我额外开一个后台线程去发送我读取的数
不然我读取变化的数进行发送,发送十分耗时,这将导致我读取不完全
什么是线程池
一组预先创建的线程,可以被重复使用来执行多个任务
一个线程的创建和销毁是十分耗时的,开销很大
当我们需要执行一个小任务时,直接找线程池要一个,做完再把线程还给他
避免频繁地创建和销毁线程,从而减少了线程创建和销毁的开销,提高了系统的性能和效率
异步编程默认使用线程池
通过异步编程可以更优雅的调用线程池,不需要我们自己去调用线程池的代码
什么是线程安全
线程安全
多个线程访问共享资源时,对共享资源的访问会导致数据不一致或产生不可预期的结果
csharp
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.异步化 异步,非同步化
考虑一下自带方法
csharp
Parallel For、ForEach、Invoke
PLINQ AsParallel、AsSequential、AsOrdered
即不需要那么底层的去实现
线程Thread
线程的创建
创建Thread
实例,并传入ThreadStart
委托
还可以配置线程,如是否为后台线程
调用Thread.Start
方法,还可以传参
线程的终止
调用Thread.Join
方法,等待线程的结束
意味着谁Join
,我就要等谁结束了再继续别的事
简言之,用于等待一个线程结束
csharp
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
锁与信号量
自己找例子
lock & Monitor
Mutex
Semaphore
WaitHandle
ManualResetEvent
AutoResetEvent 用于生产者消费者模型
ReaderWriterLock
轻量型
SemaphoreSlim
ManualResetEventSlim
ReaderWriterLockSlim
不要自己造轮子
线程安全的单例
Lazy
线程安全的集合类型
ConcurrentBag、
ConcurrentStack、ConcurrentQueue、
ConcurrentDictionary
阻塞集合
BlockingCollection
通道
Channel
原子操作
Interlocked
周期任务
PeriodicTimer