由于GIL全局解释器锁的存在,意味着在任何一个时刻,只有一个线程处于执行状态。
-
因为python是单线程的,同一时间只能执行一个方法,所以当一系列的方法被依次调用的时候,python会先解析这些方法,把其中的同步任务按照执行顺序排队到一个地方,这个地方叫做执行栈。
-
主线程之外,还存在一个"任务队列"(task queue)。当遇到异步任务时,异步任务会被挂起,继续执行执行栈中任务,等异步任务返回结果后,再按照执行顺序排列到‘’事件队列中‘’。
-
一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。如果有,就将第一个事件对应的回调推到执行栈中执行,若在执行过程中遇到异步任务,则继续将这个异步任务排列到事件队列中。
-
主线程每次将执行栈清空后,就去事件队列中检查是否有任务,如果有,就每次取出一个推到执行栈中执行,这个过程是循环往复的… …,这个过程被称为“Event Loop 事件循环”。
注:
"任务队列"是一个先进先出的数据结构,排在前面的事件,优先被主线程读取。主线程的读取过程基本上是自动的,只要执行栈一清空,"任务队列"上第一位的事件就自动进入主线程。
所谓"回调函数"(callback),就是那些会被主线程挂起来的代码。异步任务必须指定回调函数,当主线程开始执行异步任务,就是执行对应的回调函数。
“任务队列"中的事件,除了IO设备的事件以外,还包括一些用户产生的事件(比如鼠标点击、页面滚动等等)。只要指定过回调函数,这些事件发生时就会进入"任务队列”,等待主线程读取。
在Python中,异步编程和事件循环是用于提高程序效率和响应性的两个重要概念。它们与传统的多线程或多进程调度方式有所不同,并且各自有其独特的优势。
异步编程
定义:异步编程是一种编程范式,允许程序在等待某些操作(如I/O操作)完成时执行其他任务,而不是阻塞等待。这通过使用协程(coroutines)和async/await
语法实现。
优势:
- 资源效率:异步编程通常比多线程更高效,因为它不需要为每个并发任务创建独立的线程或进程。线程和进程的创建和切换开销较大。
- 简化代码逻辑:异步代码可以写得更加简洁、直观,避免了多线程中的回调地狱问题。
- 良好的性能:对于I/O密集型任务(如网络请求、文件读写),异步编程能够显著提升性能,因为CPU不会被浪费在等待I/O操作上。
事件循环
定义:事件循环是异步编程的核心机制,它负责管理任务的执行顺序,决定何时将控制权交给哪个协程。当一个协程等待某个操作完成时,事件循环会去执行其他可运行的任务,直到等待的操作完成后再返回给原来的协程。
优势:
- 高效的I/O处理:事件循环非常适合处理大量并发的I/O操作,因为它可以在单个线程中管理多个任务的状态。
- 低延迟:由于事件循环可以在不阻塞的情况下处理任务,因此它能够提供较低的延迟响应时间,这对于需要快速响应的应用(如实时系统)非常重要。
与传统线程调度的区别
- 模型不同:线程调度是基于操作系统的线程模型,每个线程都有自己的堆栈空间,并由操作系统进行调度;而异步编程依赖于事件驱动模型,所有任务在一个线程内通过事件循环进行调度。
- 适用场景:线程调度更适合于计算密集型任务,因为每个线程可以在不同的核心上并行运行;异步编程则更适合于I/O密集型任务,因为它能够有效地利用单线程处理大量并发请求。
- 复杂度:线程编程可能会遇到死锁、竞争条件等问题,而异步编程通过协程和事件循环的方式减少了这些问题的发生。
总的来说,异步编程和事件循环为解决特定类型的问题提供了高效、简洁的方法,特别是在需要处理大量并发I/O操作的场景下。