何时使用多线程
多线程解决了什么问题
- 避免阻塞:因为在单线程中,各个线程是顺序执行的,也就是说,如果某个线程阻塞了,后面的线程也会被阻塞掉。
- 避免CPU空传:在线程里,一个线程结束的标志不是将核心逻辑执行完了就可以了,例如你要请求一个数据库,不是你请求完成就可以了,这个线程还要等待数据返回,对于我们来说,等待返回是完成没有必要的,浪费CPU,为了解决这个问题,可以引入多线程,然其他线程在这个时间执行,这样CPU就不会闲着了。
- 提高效率:多线程能并发或并行执行,极大地提高了CPU利用效率,并行的话就是多个线程同一个时间启动执行,并发就是在某一段时间内有多个线程执行。
https://blog.csdn.net/weixin_47303191/article/details/124407885
多任务模型
单线程单任务:
如图,同一时刻只能做一件事。
单线程多任务:
如图,多个任务交替并发执行,一段时间内只做一个任务的一小部分,但是同一时刻只能做一件事,Unity逻辑就是使用的单线程多任务模式。
Unity逻辑的单线程多任务生命周期
多线程模型
多线程单任务:
如图,一个任务分为两部分,在两个线程中同步执行
多线程多任务:
如何安全的使用线程
线程安全与非线程安全
线程安全:
- 当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那就称这个对象时线程安全的。
- 多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。
非线程安全:在运行中不提供数据访问保护,这就可能导致多个线程先后更改数据,最后所得的数据是脏数据。
同步与锁
锁的常见类型:
- Monitor(最基础的锁,比Lock灵活)
- Lock(对Monitor的封装)
- Mutex(互斥锁,是跨系统的,一个常见的用法就是防止进程启动两个)
- ReaderWriterLockSlim(读写锁,把读写分开,读不会触发锁)
- Semaphore(信号量机制)
- Interlocker(原子锁,对变量操作)
同步的对象:
- 对象(比如一个list)
- 操作(比如读操作、写操作)
- 变量(一般使用原子锁)
注意事项:
- 只在必要时加锁,因为锁也有性能消耗
- 加锁的粒度越小越好,因为如果粒度太大,可能效率比单线程还要低
- 异步(Task)不是多线程(Thread),并发是达到并行的一种方法,并行一定要用到多核,并发可以单核异步执行