![77c303bd2cdd712b80431b65b87b74fa.png](https://img-blog.csdnimg.cn/img_convert/77c303bd2cdd712b80431b65b87b74fa.png)
多线程是一个不局限于语言的概念。多线程解决的问题是在更好的利用系统资源的情况下,比如内存、文件系统、网络等,充分利用 CPU 的空闲时间片。同一个进程的所有线程是共享同一内存的,所以不需要特殊的数据传输机制,这样不同任务之间的协调操作与运行、数据的交互、资源的分配等问题更加易于解决。
涉及到多线程就需要协调他们进行线程同步,来实现线程安全。
实现线程同步用到 5 种方式:
分别是 Lock, RLock, Semaphore, Condition, Event.
Lock 是最普通的锁,用来锁定资源。接口有两种状态上锁和解锁。适用的场景是,当有一个共同的资源需要被多个线程顺序访问时,就可以用锁。缺点是不能防止同一个线程内,锁多次上锁造成的阻塞。为了改进这种情况,就有了 RLock。
在需要标明并发读取的共享资源数量的时候,用到的就是信号量。在接口方面还是上锁和解锁两种状态。区别是在定义的时候指明共享资源的数目。适用的场景像是访问服务器这样的有限资源。当分配了一个单位的资源时,计数器值减 1,而当一个单位的资源返回资源池时,计数器值加 1。可以把它想象成在桌面上放着 n 个通行证,有想要通过的线程就拿一个,但拿完了之后,后面的线程只能等待通行证用完归还之后,才能再使用。普通的信号量在使用时,会出现被释放太多次的情况。为了改进这一情况,BoundedSemaphore 会检查内部计数器的值,并保证它不会大于初始值,如果超过初始值,就引发一个 ValueError。
在需要主动休眠,被动唤醒的场景,比如生产者-消费者问题,用到 Condition。在接口上增加了,wait 和 notify、notify_all。
Event 就像十字路口的红绿灯,当为 True 时,所有的线程都可以执行;当为 False 时,则全部阻塞。
在 Python 里的多线程模块,有 threading, multiprocessing 这样的同步模块,还有像 concurrent.futures 这样的异步多线程模块。
在 Python 里单核多线程的情况也是有应用场景的,比如 I/O 密集型的运算,当 I/O 操作对单个线程有下载上限,这时启用多线程就可以让下载的上限大幅增加。
当遇到需要频繁创建线程、进程的时候,就可以创建线程池、进程池,降低多次构造和析构带来的损耗。
为什么协程会比线程快?
线程涉及到用户态和内核态的切换;协程只在用户态之内切换上下文,并且协程会主动转交运行权。