前言:
而今同学们对“python 多并发”大致比较关切,我们都需要剖析一些“python 多并发”的相关资讯。那么小编在网上搜集了一些对于“python 多并发””的相关文章,希望我们能喜欢,大家快快来了解一下吧!预计更新
1. Python 简介
- Python 简介和历史
- Python 特点和优势
- 安装 Python
2. 变量和数据类型
- 变量和标识符
- 基本数据类型:数字、字符串、布尔值等
- 字符串操作
- 列表、元组和字典
3. 控制语句和函数
- 分支结构:if/else 语句
- 循环结构:for 和 while 循环
- 函数
- 参数传递与返回值
- Lambda 表达式
4. 模块和文件 IO
- 模块的概念
- 导入模块
- 文件 IO
- 序列化和反序列化
5. 异常处理
- 异常简介
- try/except 语句
- 自定义异常
6. 面向对象编程
- 类和对象
- 继承和多态
- 属性和方法
- 抽象类和接口
7. 正则表达式
- 正则表达式概述
- 匹配和搜索
- 替换和分割
8. 并发编程
- 多线程
- 多进程
- 协程和异步编程
9. 数据库编程
- 关系型数据库介绍
- 使用 SQLite 数据库
- 使用 MySQL 数据库
- 使用 PostgreSQL 数据库
10. 网络编程
- Socket 编程简介
- TCP Socket 编程
- UDP Socket 编程
- HTTP 编程
11. Web 开发框架 Flask
- Flask 简介
- 安装 Flask
- 路由和视图函数
- 模板和静态文件
12. 数据分析和科学计算
- NumPy 基础
- Pandas 基础
- Matplotlib 基础
13. 机器学习入门
- 机器学习概述
- 监督学习和非监督学习
- Scikit-Learn 简介
- 利用 Scikit-Learn 进行数据预处理和模型训练
14. 自然语言处理
- 自然语言处理概述
- 中文分词和处理
- 文本分类和情感分析
15. 游戏开发与 Pygame
- Pygame 简介
- Pygame 基础
- 开发一个简单的游戏
8. 并发编程
- 多线程
- 多进程
- 协程和异步编程
多线程
Python多线程是指使用Python编写并发程序时,通过创建多个线程来提高程序的执行效率。多线程可以让程序在同一时间内同时处理多个任务,从而提高程序的运行速度和响应能力。在Python中,多线程的实现主要依赖于threading模块。
1. 线程和进程的区别
在开始讨论Python多线程之前,需要先了解线程和进程的概念以及它们之间的区别。
进程是操作系统资源分配的基本单位,每个进程都有自己独立的地址空间,并占用着一定的系统资源(如CPU、内存等)。进程与进程之间是相互独立的,一个进程崩溃或者被杀死不会影响到其他进程。
线程是进程中的执行单元,一个进程可以包含多个线程。线程之间共享进程资源,每个线程有自己的栈和局部变量,但是它们共享全局变量、静态变量等。不同线程之间切换的开销比进程之间切换的开销小得多。
2. Python threading模块
Python提供了threading模块来支持多线程编程。该模块提供了Thread类来创建线程,常用的方法有:
`Thread(target=, args=)`:创建新的线程。
`start()`:启动线程。
`join([time])`:等待线程运行结束。
`is_alive()`:判断线程是否在运行。
3. 创建线程
创建一个新的线程需要使用Thread类,它的构造函数如下:
```
Thread(target=, args=(), name=)
```
其中,`target`参数为该线程所要执行的目标函数。如果不指定`name`,则每个线程会自动生成一个唯一的名字。
示例代码如下:
```python
import threading
import time
# 定义线程处理函数
def thread_func(thread_id):
print('Thread %d is running...' % thread_id)
time.sleep(2) # 模拟线程执行时间
print('Thread %d is done.' % thread_id)
# 创建5个线程并启动
for i in range(5):
t = threading.Thread(target=thread_func, args=(i,))
t.start()
# 等待所有线程运行结束
for t in threading.enumerate():
if t != threading.current_thread():
t.join()
```
上面的代码中,我们首先定义了一个线程处理函数`thread_func`,它接受一个参数`thread_id`,用于标识当前线程。然后我们使用`threading.Thread`类创建了5个线程,并分别传递给它们不同的`thread_id`参数。
通过调用`start()`方法来启动线程,这将会调用线程处理函数。主线程继续往下执行,而新产生的线程在后台运行。
最后,我们使用`enumerate()`方法获取所有的线程,并调用`join()`方法等待它们运行结束。
4. 线程同步
在多线程编程中,线程之间会共享一些数据,如果多个线程同时修改同一个变量可能会导致不可预期的结果。所以需要对共享资源进行同步处理。
Python提供了Lock、RLock、Semaphore、Event、Condition等同步机制来实现线程同步。其中,Lock和RLock都是互斥锁,只允许一个线程访问被保护的共享资源;Semaphore是信号量,允许多个线程同时访问某个资源;Event可以用于线程之间通信,一个线程可以通过set()方法发出事件,其他线程可以通过wait()方法等待该事件的发生;Condition可以用于控制线程执行的顺序,它可以让某些线程等待特定条件的发生,再继续执行。
下面我们分别介绍这些同步机制的使用方法。
4.1 Lock
Lock是最简单也是最常用的同步机制,它的作用是保证对共享资源的互斥访问。
在Python中,Lock可以通过threading模块来创建:
```python
lock = threading.Lock()
```
然后在需要保护的代码块前后加上acquire()和release()方法,如下所示:
```python
import threading
# 共享变量
count = 0
# 创建锁
lock = threading.Lock()
# 线程处理函数
def thread_func():
global count, lock
with lock:
for i in range(100000):
count += 1
# 创建10个线程并启动
threads = []
for i in range(10):
t = threading.Thread(target=thread_func)
threads.append(t)
t.start()
# 等待所有线程运行结束
for t in threads:
t.join()
# 输出count的值
print('count=%d' % count)
```
上面的代码中,我们先定义了一个全局变量`count`,然后使用`threading.Lock()`创建了一个锁对象`lock`。在线程处理函数中,我们通过`with lock:`语句获取锁,然后对`count`进行累加操作。
由于多个线程会同时竞争同一个锁,所以只有一个线程能够获得锁,并执行累加操作。其他线程则会阻塞在`with lock:`语句处,等待锁的释放。
最后,我们通过输出`count`的值检查程序的正确性。
4.2 RLock
RLock是可重入锁,它允许同一个线程多次获取锁。这对于一些需要递归调用的场景非常有用。其使用方法与Lock类似,唯一的区别就是可以多次acquire()。
```python
import threading
# 共享变量
count = 0
# 创建锁
lock = threading.RLock()
# 线程处理函数
def thread_func():
global count, lock
with lock:
with lock: # 多次acquire()
for i in range(100000):
count += 1
# 创建10个线程并启动
threads = []
for i in range(10):
t = threading.Thread(target=thread_func)
threads.append(t)
t.start()
# 等待所有线程运行结束
for t in threads:
t.join()
# 输出count的值
print('count=%d' % count)
```
上面的代码中,我们创建了一个RLock对象`lock`,然后在线程处理函数中使用多次`with lock:`语句来演示RLock的使用方法。
4.3 Semaphore
Semaphore是信号量,它控制对共享资源的访问数量。当一个线程获得了信号量之后,其他线程必须等待该线程释放信号量后才能继续执行。
在Python中,Semaphore可以通过threading模块来创建:
```python
semaphore = threading.Semaphore(value)
```
其中`value`参数表示信号量的初始值,默认为1。然后我们可以使用acquire()和release()方法来获取和释放信号量。
```python
import threading
# 共享变量
count = 0
# 创建信号量
semaphore = threading.Semaphore(value=5)
# 线程处理函数
def thread_func():
global count, semaphore
with semaphore: # 获取信号量
for i in range(100000):
count += 1
semaphore.release() # 释放信号量
# 创建10个线程并启动
threads = []
for i in range(10):
t = threading.Thread(target=thread_func)
threads.append(t)
t.start()
# 等待所有线程运行结束
for t in threads:
t.join()
# 输出count的值print('count=%d' % count)
上面的代码中,我们先创建了一个Semaphore对象`semaphore`,将其初始值设为5。然后在线程处理函数中使用`with semaphore:`语句来获取信号量,执行累加操作后再释放信号量。
由于Semaphore的初始值为5,所以最多只有5个线程可以同时执行累加操作,其他线程必须等待信号量的释放才能继续执行。
4.4 Event
Event是用于线程之间通信的同步机制,它允许一个线程发出事件,其他线程等待该事件的发生。在Python中,Event可以通过threading模块来创建:
```python
evt = threading.Event()
```
然后我们可以使用set()方法发出事件,使用wait()方法等待事件的发生。
```python
import threading
# 创建事件
evt = threading.Event()
# 线程1处理函数
def thread_func1():
print('Thread 1 is waiting...')
evt.wait() # 等待事件发生
print('Thread 1 is done.')
# 线程2处理函数
def thread_func2():
print('Thread 2 is running...')
for i in range(5):
print('Thread 2 is working...')
time.sleep(1)
evt.set() # 发出事件
print('Thread 2 is done.')
# 创建线程并启动
t1 = threading.Thread(target=thread_func1)
t2 = threading.Thread(target=thread_func2)
t1.start()
t2.start()
# 等待所有线程运行结束
t1.join()
t2.join()
```
上面的代码中,我们创建了一个Event对象`evt`。在线程1处理函数中,我们使用`evt.wait()`语句等待事件发生;在线程2处理函数中,我们使用`evt.set()`方法发出事件。
由于线程1等待事件的发生,所以会一直阻塞在`evt.wait()`语句处,直到线程2发出事件才能继续执行。
4.5 Condition
Condition是控制线程执行顺序的同步机制,它可以让某些线程等待特定条件的发生,再继续执行。
在Python中,Condition可以通过threading模块来创建:
```python
cond = threading.Condition(lock=None)
```
其中`lock`参数表示该Condition使用的锁,默认为RLock。
然后我们可以使用wait()、notify()和notify_all()方法来控制线程之间的执行顺序。
```python
import threading
# 共享变量
count = 0
# 创建条件变量
cond = threading.Condition()
# 线程1处理函数
def thread_func1():
global count, cond
with cond:
while count < 5:
print('Thread 1 is waiting...')
cond.wait() # 等待条件变量
print('Thread 1 is done.')
# 线程2处理函数
def thread_func2():
global count, cond
with cond:
for i in range(5):
count += 1
print('Thread 2 is working...')
time.sleep(1)
cond.notify() # 通知条件变量已经满足
print('Thread 2 is done.')
# 创建线程并启动
t1 = threading.Thread(target=thread_func1)
t2 = threading.Thread(target=thread_func2)
t1.start()
t2.start()
# 等待所有线程运行结束
t1.join()
t2.join()
```
上面的代码中,我们创建了一个Condition对象`cond`。在线程1处理函数中,我们使用`while count < 5:`语句等待条件变量;在线程2处理函数中,我们通过累加操作使得条件变量满足,并使用`cond.notify()`方法通知等待的线程。
由于线程1等待条件变量的发生,所以会一直阻塞在`cond.wait()`语句处,直到线程2发出通知才能继续执行。
5. 线程安全问题
多线程编程中最常见的问题就是线程安全问题。由于多个线程可能同时访问共享资源,所以如果不加以保护,就会出现数据不一致等问题。
在Python中,我们可以使用锁、信号量、条件变量等同步机制来解决线程安全问题。具体来说,我们可以按照以下步骤来实现线程安全:
1. 定义共享资源并初始化。
2. 创建互斥锁或信号量等同步机制。
3. 在对共享资源进行操作前获取锁或信号量。
4. 对共享资源进行操作。
5. 释放锁或信号量。
例如,在第4节中,我们使用锁和信号量等同步机制来保护了`count`这个共享变量,避免了多个线程同时修改它而引发的线程安全问题。
另外,为了避免死锁问题,我们还需要注意同步机制的使用方法。一般来说,当多个线程需要获取多个锁时,我们应该按照固定的顺序获取锁,避免死锁的发生。
6. 总结
本文介绍了Python中的多线程编程,并详细讲解了线程的创建、启动、结束等基本操作,以及同步机制的使用方法。同时,我们还介绍了线程安全问题的解决方法,包括锁、信号量、条件变量等同步机制的使用。
最后,需要注意的是,在进行多线程编程时,我们应该尽可能地避免共享资源的使用,尽量使用局部变量等本地数据,从而减少线程之间的竞争和冲突。
多进程
一、概述
多进程是指在操作系统中,同时运行多个进程,每个进程都有自己的独立地址空间,它们之间相互独立,互不影响。多进程编程可以充分利用多核CPU的性能优势,提高程序的执行效率。
Python标准库中提供了`multiprocessing`模块,用于支持多进程编程。该模块提供了Process、Queue、Pipe等类和函数,方便我们创建和管理子进程,以及进行进程间通信。
本文将从以下几个方面介绍Python多进程编程:
1. 进程的基本概念
2. 创建和启动进程
3. 进程间通信
4. 进程池
5. 多进程编程实践
二、进程的基本概念
进程是操作系统中的一个基本概念,它表示正在执行的程序。每个进程都有自己的独立地址空间,包括代码段、数据段、堆栈等。进程之间相互独立,互不干扰。
在Linux系统中,我们可以使用`ps`命令查看当前系统中正在运行的所有进程:
```
$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 1 0.0 0.2 19060 2520 ? Ss Mar22 0:03 /sbin/init
root 2 0.0 0.0 0 0 ? S Mar22 0:00 [kthreadd]
root 3 0.0 0.0 0 0 ? I< Mar22 0:00 [rcu_gp]
root 4 0.0 0.0 0 0 ? I< Mar22 0:00 [rcu_par_gp]
...
```
其中,PID表示进程的ID,COMMAND表示进程的命令。
在Python中,我们可以使用`os`模块来获取当前进程的ID和父进程的ID:
```python
import os
print('Current process ID:', os.getpid())
print('Parent process ID:', os.getppid())
```
输出结果如下:
```
Current process ID: 12345
Parent process ID: 67890
```
三、创建和启动进程
在Python中,我们可以通过`multiprocessing`模块来创建和启动子进程。`multiprocessing`模块提供了Process类,用于表示一个进程对象。
下面是一个简单的例子,演示如何使用Process类创建和启动子进程:
```python
from multiprocessing import Process
import os
# 子进程执行的代码
def child_proc(name):
print('Child process %s (%s) running...' % (name, os.getpid()))
if __name__ == '__main__':
# 创建子进程并启动
p = Process(target=child_proc, args=('test',))
print('Parent process %s.' % os.getpid())
p.start()
p.join()
print('Child process end.')
```
上面的代码中,我们首先定义了一个函数`child_proc`,用于表示子进程要执行的代码。然后,在主进程中,我们使用Process类创建了一个子进程对象`p`,并通过`start()`方法启动子进程。
注意到我们在创建子进程之前添加了一句`if __name__ == '__main__':`,这是因为在Windows系统中,由于多进程模块会将整个程序复制一份作为新的进程运行,如果没有加上这句判断语句,就会导致无限递归。
输出结果如下:
```
Parent process 12345.
Child process test (67890) running...
Child process end.
```
从输出结果可以看出,子进程已经成功创建并运行,打印出了子进程的ID和父进程的ID。
四、进程间通信
在多进程编程中,由于每个进程都有自己独立的地址空间,所以它们之间无法直接访问彼此的变量。为了实现进程间的数据共享和通信,我们需要使用进程间通信(IPC)机制。
`multiprocessing`模块提供了多种进程间通信方式,包括队列、管道、共享内存等。这里我们主要介绍Queue和Pipe两种方式。
4.1 Queue
Queue是一种线程安全的队列,可以用于多进程之间的通信。在Python中,我们可以使用`multiprocessing.Queue`类来创建一个队列对象。
下面是一个简单的例子,演示如何使用Queue进行进程间通信:
```python
from multiprocessing import Process, Queue
# 子进程执行的代码
def child_proc(q):
value = q.get() # 从队列中获取数据
print('Child process received:', value)
if __name__ == '__main__':
# 创建队列并传递给子进程
q = Queue()
p = Process(target=child_proc, args=(q,))
# 往队列中放入数据
q.put('Hello, world!')
# 启动子进程
p.start()
p.join()
```
上面的代码中,我们首先创建了一个Queue对象`q`,然后通过Process类创建了一个子进程对象`p`。在主进程中,我们使用`q.put()`方法往队列中放入了一条数据。
在子进程中,我们使用`q.get()`方法获取队列中的数据,并打印出来。
输出结果如下:
```
Child process received: Hello, world!
```
可以看到,子进程成功地从队列中取出了数据并打印出来。
4.2 Pipe
Pipe是一种双向管道,可以用于两个进程之间的通信。在Python中,我们可以使用`multiprocessing.Pipe`类来创建一个管道对象。
下面是一个简单的例子,演示如何使用Pipe进行进程间通信:
```python
from multiprocessing import Process, Pipe
# 子进程执行的代码
def child_proc(conn):
data = conn.recv() # 接收主进程发送的数据
print('Child process received:', data)
conn.send('Hello, world!') # 向主进程发送数据
if __name__ == '__main__':
# 创建管道并传递给子进程
parent_conn, child_conn = Pipe()
p = Process(target=child_proc, args=(child_conn,))
# 向子进程发送数据
parent_conn.send('Hello, child!')
# 启动子进程
p.start()
# 接收子进程返回的数据
data = parent_conn.recv()
print('Parent process received:', data)
p.join()
```
上面的代码中,我们首先使用`multiprocessing.Pipe()`函数创建了一对管道对象`parent_conn`和`child_conn`,然后通过Process类创建了一个子进程对象`p`。在主进程中,我们使用`parent_conn.send()`方法向子进程发送了一条数据。
在子进程中,我们使用`conn.recv()`方法接收主进程发送的数据,并使用`conn.send()`方法向主进程发送了一条数据。
在主进程中,我们使用`parent_conn.recv()`方法接收子进程返回的数据,并打印出来。
输出结果如下:
```
Child process received: Hello, child!
Parent process received: Hello, world!
```
可以看到,主进程成功地向子进程发送了一条数据,并从子进程接收了一条数据。
五、进程池
在多进程编程中,为避免频繁创建和销毁进程带来的系统开销,我们可以使用进程池(Pool)来管理进程,以便重复利用已创建的进程。
`multiprocessing`模块提供了Pool类,用于创建一个进程池对象。我们可以通过调用该对象的`apply()`、`apply_async()`、`map()`、`imap()`等方法来提交任务,并返回结果。
下面是一个简单的例子,演示如何使用进程池执行任务:
```python
from multiprocessing import Pool
import time
# 计算平方数
def square(n):
print('calculate square of %d' % n)
time.sleep(1)
return n * n
if __name__ == '__main__':
# 创建进程池对象
with Pool(processes=3) as pool:
# 调用map方法执行任务
results = pool.map(square, [1, 2, 3, 4, 5])
print(results)
```
上面的代码中,我们首先定义了一个函数`square`,用于计算一个数的平方数。在主进程中,我们使用`multiprocessing.Pool()`函数创建了一个进程池对象`pool`,并指定了进程数为3。然后,我们调用`pool.map()`方法提交任务,并使用`print()`打印出返回结果。
输出结果如下:
```
calculate square of 1
calculate square of 2
calculate square of 3
calculate square of 4
calculate square of 5
[1, 4, 9, 16, 25]
```
可以看到,进程池成功地执行了我们提交的任务,并返回了预期的结果。
除了map方法外,进程池还提供了apply()、apply_async()、imap()等方法,具体用法可以参考官方文档。
六、多进程编程实践
在实际应用中,我们经常会遇到需要大量计算的任务,比如图像处理、机器学习等。使用多进程可以极大地提高计算效率,加快任务完成速度。下面是一个简单的例子,演示如何使用多进程进行图像处理:
```python
from PIL import Image
import numpy as np
from multiprocessing import Pool
# 图像缩放函数
def resize(img_path, size):
img = Image.open(img_path)
img = img.resize(size)
return img
if __name__ == '__main__':
# 加载图像列表
img_list = ['image1.jpg', 'image2.jpg', 'image3.jpg', 'image4.jpg']
# 创建进程池对象
with Pool(processes=4) as pool:
# 调用map方法执行任务
results = pool.starmap(resize, [(img_path, (512, 512)) for img_path in img_list])
# 将结果保存到文件
for i, img in enumerate(results):
img.save('result%d.jpg' % (i+1))
```
上面的代码中,我们首先定义了一个函数`resize`,用于对一张图片进行缩放操作。然后,我们使用`PIL.Image.open()`函数加载了4张图片,并使用进程池对它们进行缩放操作。最后,我们将结果保存到文件中。
由于进程池使用了4个进程同时进行图像处理,因此可以大大提高图像处理效率。
七、总结
本文介绍了Python中的多进程编程,并详细讲解了进程的基本概念、如何创建和启动进程、进程间通信、进程池等内容。同时,我们还演示了一个简单的实例,展示了如何使用多进程来加速图像处理任务。
多进程编程是Python中非常重要的话题之一,掌握多进程编程对于开发高性能、高并发应用具有重要意义。希望本文能够帮助读者更好地理解并掌握Python多进程编程技巧。
协程和异步编程
一、异步编程概述
异步编程是一种并发处理的方式,它在单线程下实现多个任务同时执行的效果,从而提高程序的整体性能。在传统的同步编程模型中,一个任务必须等待另一个任务完成后才能继续执行,这种模型会造成大量的 CPU 时间浪费。而在异步编程模型中,任务可以像轮流使用 CPU 一样交替执行,从而避免了 CPU 的空闲时间。
Python 的异步编程主要通过 asyncio 模块来实现,它提供了对协程的支持,使得开发者可以用比较简单的语法来编写高效的异步程序。在 asyncio 中,协程是异步编程的基本单位,可以理解为一个特殊的函数。
二、协程概述
协程是一种能够暂停执行并在需要时恢复执行的函数,它不同于线程和进程的异步编程方式。协程可以看作是一种用户级线程,也称为“微线程”,通常运行在单线程中,由协程调度程序切换执行。
Python 3.4 引入了 asyncio 库,内置了协程功能,此后 Python 开始支持原生的协程编程。在 Python 3.5 中,新引入了 async 和 await 两个关键字,更加方便了协程的编写。具体来说,async 关键字用于声明一个函数为协程函数,而 await 关键字用于暂停当前协程的执行,等待其他协程或者异步任务执行完成后再继续执行。
在 asyncio 中,协程由 asyncio.coroutine 装饰器进行修饰,这样被修饰的函数就可以使用 await 关键字暂停当前协程的执行了。下面是一个简单的协程示例:
```python
import asyncio
async def coroutine():
print("start coroutine")
await asyncio.sleep(1)
print("end coroutine")
# 初始化事件循环
loop = asyncio.get_event_loop()
# 执行协程
loop.run_until_complete(coroutine())
```
上面的代码中,我们首先定义了一个名为 coroutine 的协程函数,它打印出一条开始执行的消息,并调用 asyncio.sleep() 函数让协程挂起 1 秒钟,最后打印出一条结束执行的消息。
在主程序中,我们通过 asyncio.get_event_loop() 函数获取到一个事件循环对象,然后调用该对象的 run_until_complete() 方法来运行协程。
三、协程与线程的比较
协程和线程都可以实现异步编程,但二者有很大的不同之处。下面是协程与线程的比较:
1. 状态切换方式不同:线程是由操作系统内核来进行调度的,而协程则是由程序员自己实现的调度方式。
2. 调度开销不同:线程切换时需要保存当前上下文并在下一次切换时恢复,这个过程需要进行上下文切换和内核态和用户态之间的转换,因此比较耗费 CPU 时间。而协程的上下文切换仅仅是程序栈的切换,没有内核态和用户态之间的切换,因此非常快速。
3. 并发性不同:线程是操作系统级别的并发,可以利用多核 CPU 实现真正的并行处理;而协程则是单线程的并发,它不能充分利用多核 CPU 的优势,但有着更高的并发性能。
4. 编写难度不同:线程编程的难度较大,需要考虑线程安全和锁的问题,而协程编程则相对容易一些,不需要考虑这些问题。
综合来看,协程比线程更适用于 I/O 密集型应用程序,而线程则适用于 CPU 密集型应用程序。
四、asyncio 库的使用
1. 事件循环
事件循环是 asyncio 的核心组件,它可以在单线程下运行多个协程,并负责调度它们的执行。每个 asyncio 程序必须有一个事件循环对象,我们可以通过 asyncio.get_event_loop() 函数获取到它。
```python
import asyncio
# 获取事件循环对象
loop = asyncio.get_event_loop()
# 执行协程
loop.run_until_complete(coroutine())
```
2. 协程函数
协程函数是由 async 关键字修饰的函数,它可以被 await 关键字挂起和恢复执行。下面是一个简单的协程函数示例:
```python
import asyncio
async def coroutine():
print("start coroutine")
await asyncio.sleep(1)
print("end coroutine")
```
3. 异步任务
异步任务是一个可等待对象,它可以被事件循环挂起和恢复执行。在 asyncio 中,常见的异步任务包括协程、Future 对象、Task 对象等。下面是异步任务的一个简单示例:
```python
import asyncio
async def coroutine():
print("start coroutine")
await asyncio.sleep(1)
print("end coroutine")
async def async_func():
print("start async function")
await coroutine()
print("end async function")
# 获取事件循环对象
loop = asyncio.get_event_loop()
# 执行异步任务
loop.run_until_complete(async_func())
```
4. Future 对象
Future 是一个代表异步操作结果的对象,它可以被用于协程间通信和协调。如果一个协程需要等待另一个协程完成某个操作后才能继续执行,那么它可以通过 Future 对象来实现。下面是一个使用 Future 对象进行协程间通信的示例:
```python
import asyncio
async def coroutine(future):
print("start coroutine")
await asyncio.sleep(1)
future.set_result("hello world")
print("end coroutine")
async def async_func():
print("start async function")
future = asyncio.Future()
asyncio.ensure_future(coroutine(future))
result = await future
print(result)
# 获取事件循环对象
loop = asyncio.get_event_loop()
# 执行异步任务
loop.run_until_complete(async_func())
```
在上面的代码中,我们首先定义了一个 coroutine 协程函数,并传入了一个 future 对象。在该函数内部,我们使用 asyncio.sleep() 函数模拟一个耗时的操作,并使用 future.set_result() 方法设置异步操作的返回值。在 async_func 函数中,我们监听 future 对象的完成状态,并在异步操作完成后打印出返回值。
5. Task 对象
Task 对象是对 Future 对象的进一步封装,它可以方便地对多个协程进行调度和管理。Task 对象通常是通过 asyncio.create_task() 函数创建的。下面是一个使用 Task 对象进行协程调度的示例:
```python
import asyncio
async def coroutine(name):
print("start %s" % name)
await asyncio.sleep(1)
print("end %s" % name)
async def async_func():
tasks = [asyncio.create_task(coroutine("coroutine%d" % i)) for i in range(3)]
for task in tasks:
await task
# 获取事件循环对象
loop = asyncio.get_event_loop()
# 执行异步任务
loop.run_until_complete(async_func())
```
在上面的代码中,我们先定义了一个 coroutine 协程函数,并传入了一个名字参数。然后,在 async_func 函数内部,我们使用 asyncio.create_task() 函数创建了三个协程任务,并将它们添加到任务列表中。最后,我们使用 for 循环逐个执行每个任务。
6. 同步与异步的混合使用
在实际应用中,我们常常需要在异步程序中调用同步函数或者阻塞操作,这时候就需要使用 asyncio 的一些辅助函数来避免阻塞事件循环。下面是一些常用的 asyncio 辅助函数:
- asyncio.run_in_executor():在另一个线程或进程中执行函数,并将结果返回给当前协程。
```python
import asyncio
import requests
async def coroutine():
print("start coroutine")
loop = asyncio.get_running_loop()
future = loop.run_in_executor(None, requests.get, ";)
response = await future
print(response.status_code)
print("end coroutine")
# 获取事件循环对象
loop = asyncio.get_event_loop()
# 执行协程
loop.run_until_complete(coroutine())
```
- asyncio.sleep():模拟一个耗时的操作,并挂起当前协程的执行。
```python
import asyncio
async def coroutine():
print("start coroutine")
await asyncio.sleep(1)
print("end coroutine")
# 获取事件循环对象
loop = asyncio.get_event_loop()
# 执行协程
loop.run_until_complete(coroutine())
```
- asyncio.wait():等待多个协程任务完成后再继续执行。
```python
import asyncio
async def coroutine(name):
print("start %s" % name)
await asyncio.sleep(1)
print("end %s" % name)
async def async_func():
tasks = [asyncio.create_task(coroutine("coroutine%d" % i)) for i in range(3)]
await asyncio.wait(tasks)
# 获取事件循环对象
loop = asyncio.get_event_loop()
# 执行异步任务
loop.run_until_complete(async_func())
```
五、总结
本文介绍了 Python 异步编程的基本概念,包括协程、事件循环、异步任务、Future 对象和 Task 对象等。我们还比较了协程与线程的不同之处,并且讲解了如何在异步程序中使用同步函数或阻塞操作。
异步编程可以大幅度提高程序的并发性能,特别适用于 I/O 密集型应用程序。Python 的 asyncio 模块为开发者提供了非常方便的协程支持,使得编写高效的异步程序变得简单易行。同时,在实际应用中需谨慎使用异步编程,避免造成过度使用导致的代码维护困难和可读性差等问题。
精彩继续:「链接」
标签: #python 多并发