何时使用多线程

多线程解决了什么问题

  • 避免阻塞:因为在单线程中,各个线程是顺序执行的,也就是说,如果某个线程阻塞了,后面的线程也会被阻塞掉。
  • 避免CPU空传:在线程里,一个线程结束的标志不是将核心逻辑执行完了就可以了,例如你要请求一个数据库,不是你请求完成就可以了,这个线程还要等待数据返回,对于我们来说,等待返回是完成没有必要的,浪费CPU,为了解决这个问题,可以引入多线程,然其他线程在这个时间执行,这样CPU就不会闲着了。
  • 提高效率:多线程能并发或并行执行,极大地提高了CPU利用效率,并行的话就是多个线程同一个时间启动执行,并发就是在某一段时间内有多个线程执行。

https://blog.csdn.net/weixin_47303191/article/details/124407885

多任务模型

单线程单任务:

image-20221003143208118

如图,同一时刻只能做一件事。

单线程多任务:

image-20221003143335882

如图,多个任务交替并发执行,一段时间内只做一个任务的一小部分,但是同一时刻只能做一件事,Unity逻辑就是使用的单线程多任务模式。

image-20221003143335883

Unity逻辑的单线程多任务生命周期

多线程模型

多线程单任务:

image-20221003144548665

如图,一个任务分为两部分,在两个线程中同步执行

多线程多任务:

image-20221003145022938

如何安全的使用线程

线程安全与非线程安全

线程安全:

  • 当多个线程同时访问一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获得正确的结果,那就称这个对象时线程安全的。
  • 多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。

非线程安全:在运行中不提供数据访问保护,这就可能导致多个线程先后更改数据,最后所得的数据是脏数据。

同步与锁

锁的常见类型:

  • Monitor(最基础的锁,比Lock灵活)
  • Lock(对Monitor的封装)
  • Mutex(互斥锁,是跨系统的,一个常见的用法就是防止进程启动两个)
  • ReaderWriterLockSlim(读写锁,把读写分开,读不会触发锁)
  • Semaphore(信号量机制)
  • Interlocker(原子锁,对变量操作)

同步的对象:

  • 对象(比如一个list)
  • 操作(比如读操作、写操作)
  • 变量(一般使用原子锁)

注意事项:

  • 只在必要时加锁,因为锁也有性能消耗
  • 加锁的粒度越小越好,因为如果粒度太大,可能效率比单线程还要低
  • 异步(Task)不是多线程(Thread),并发是达到并行的一种方法,并行一定要用到多核,并发可以单核异步执行

如何向纯洁的女朋友解释并发与并行的区别?

如何理解互斥锁、条件锁、读写锁以及自旋锁?

并发和并行、同步和异步及多线程概念详解

C#多线程系列之多线程锁lock和Monitor