
在现代网络应用开发中,高并发处理能力是衡量系统性能的重要指标。Python的asyncio库自3.4版本引入以来,已经成为构建高性能异步应用的标准方案。无论是Web服务器、爬虫系统还是微服务架构,asyncio都能以极低的资源开销实现高效的I/O并发。本文将从事件循环原理出发,结合实际代码示例,帮助你全面掌握Python asyncio异步编程。
一、asyncio的核心概念:事件循环与协程
asyncio的核心是事件循环(Event Loop),它负责调度和执行所有的异步任务。协程(Coroutine)是使用
1 | async def |
定义的函数,调用后不会立即执行,而是返回一个协程对象,需要通过事件循环来驱动运行。
1
2
3
4
5
6
7
8
9
10
11
12 <span class="kn">import</span><span class="w"> </span><span class="nn">asyncio</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">say_hello</span><span class="p">(</span><span class="n">name</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">delay</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">delay</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Hello, </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">! (after </span><span class="si">{</span><span class="n">delay</span><span class="si">}</span><span class="s2">s)"</span><span class="p">)</span>
<span class="k">return</span> <span class="sa">f</span><span class="s2">"greeted </span><span class="si">{</span><span class="n">name</span><span class="si">}</span><span class="s2">"</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">main</span><span class="p">():</span>
<span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">say_hello</span><span class="p">(</span><span class="s2">"World"</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"Result: </span><span class="si">{</span><span class="n">result</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">asyncio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
1 | await |
关键字是协程的关键,它将控制权交还给事件循环,让其他协程有机会执行。当await的操作完成后,事件循环会恢复该协程的执行。
二、并发执行多个任务:gather与TaskGroup
实际开发中,我们通常需要同时执行多个异步操作。
1 | asyncio.gather() |
是最常用的并发执行方式,它会同时启动所有协程并等待全部完成。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 <span class="kn">import</span><span class="w"> </span><span class="nn">asyncio</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">time</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">fetch_data</span><span class="p">(</span><span class="n">url</span><span class="p">:</span> <span class="nb">str</span><span class="p">,</span> <span class="n">delay</span><span class="p">:</span> <span class="nb">int</span><span class="p">)</span> <span class="o">-></span> <span class="nb">dict</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"开始请求: </span><span class="si">{</span><span class="n">url</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="n">delay</span><span class="p">)</span>
<span class="k">return</span> <span class="p">{</span><span class="s2">"url"</span><span class="p">:</span> <span class="n">url</span><span class="p">,</span> <span class="s2">"status"</span><span class="p">:</span> <span class="mi">200</span><span class="p">,</span> <span class="s2">"delay"</span><span class="p">:</span> <span class="n">delay</span><span class="p">}</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">main</span><span class="p">():</span>
<span class="n">start</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span>
<span class="n">urls</span> <span class="o">=</span> <span class="p">[</span>
<span class="p">(</span><span class="s2">"https://api.example.com/users"</span><span class="p">,</span> <span class="mi">2</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"https://api.example.com/posts"</span><span class="p">,</span> <span class="mi">1</span><span class="p">),</span>
<span class="p">(</span><span class="s2">"https://api.example.com/comments"</span><span class="p">,</span> <span class="mi">3</span><span class="p">),</span>
<span class="p">]</span>
<span class="n">tasks</span> <span class="o">=</span> <span class="p">[</span><span class="n">fetch_data</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">delay</span><span class="p">)</span> <span class="k">for</span> <span class="n">url</span><span class="p">,</span> <span class="n">delay</span> <span class="ow">in</span> <span class="n">urls</span><span class="p">]</span>
<span class="n">results</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">gather</span><span class="p">(</span><span class="o">*</span><span class="n">tasks</span><span class="p">)</span>
<span class="n">elapsed</span> <span class="o">=</span> <span class="n">time</span><span class="o">.</span><span class="n">time</span><span class="p">()</span> <span class="o">-</span> <span class="n">start</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"总耗时: </span><span class="si">{</span><span class="n">elapsed</span><span class="si">:</span><span class="s2">.1f</span><span class="si">}</span><span class="s2">s(而非串行的6s)"</span><span class="p">)</span>
<span class="k">for</span> <span class="n">r</span> <span class="ow">in</span> <span class="n">results</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">" </span><span class="si">{</span><span class="n">r</span><span class="p">[</span><span class="s1">'url'</span><span class="p">]</span><span class="si">}</span><span class="s2"> -> </span><span class="si">{</span><span class="n">r</span><span class="p">[</span><span class="s1">'status'</span><span class="p">]</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="n">asyncio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
Python 3.11引入了 ,它提供了更安全的并发管理,能自动处理异常传播:
1
2
3
4
5 <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">main_taskgroup</span><span class="p">():</span>
<span class="k">async</span> <span class="k">with</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">TaskGroup</span><span class="p">()</span> <span class="k">as</span> <span class="n">tg</span><span class="p">:</span>
<span class="n">task1</span> <span class="o">=</span> <span class="n">tg</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">fetch_data</span><span class="p">(</span><span class="s2">"https://api.a.com"</span><span class="p">,</span> <span class="mi">2</span><span class="p">))</span>
<span class="n">task2</span> <span class="o">=</span> <span class="n">tg</span><span class="o">.</span><span class="n">create_task</span><span class="p">(</span><span class="n">fetch_data</span><span class="p">(</span><span class="s2">"https://api.b.com"</span><span class="p">,</span> <span class="mi">1</span><span class="p">))</span>
<span class="nb">print</span><span class="p">(</span><span class="n">task1</span><span class="o">.</span><span class="n">result</span><span class="p">(),</span> <span class="n">task2</span><span class="o">.</span><span class="n">result</span><span class="p">())</span>
三、异步上下文管理器与异步迭代器
asyncio不仅支持异步函数,还支持异步上下文管理器(
1 | async with |
)和异步迭代器(
1 | async for |
),这在管理数据库连接池、消息队列等资源时非常实用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 <span class="kn">import</span><span class="w"> </span><span class="nn">asyncio</span>
<span class="k">class</span><span class="w"> </span><span class="nc">AsyncConnectionPool</span><span class="p">:</span>
<span class="k">def</span><span class="w"> </span><span class="fm">__init__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">max_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">5</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">max_size</span> <span class="o">=</span> <span class="n">max_size</span>
<span class="bp">self</span><span class="o">.</span><span class="n">_pool</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">Queue</span><span class="p">(</span><span class="n">maxsize</span><span class="o">=</span><span class="n">max_size</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">connect</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">max_size</span><span class="p">):</span>
<span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pool</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="sa">f</span><span class="s2">"conn_</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="fm">__aenter__</span><span class="p">(</span><span class="bp">self</span><span class="p">):</span>
<span class="bp">self</span><span class="o">.</span><span class="n">conn</span> <span class="o">=</span> <span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pool</span><span class="o">.</span><span class="n">get</span><span class="p">()</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"获取连接: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">conn</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">return</span> <span class="bp">self</span><span class="o">.</span><span class="n">conn</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="fm">__aexit__</span><span class="p">(</span><span class="bp">self</span><span class="p">,</span> <span class="n">exc_type</span><span class="p">,</span> <span class="n">exc_val</span><span class="p">,</span> <span class="n">exc_tb</span><span class="p">):</span>
<span class="k">await</span> <span class="bp">self</span><span class="o">.</span><span class="n">_pool</span><span class="o">.</span><span class="n">put</span><span class="p">(</span><span class="bp">self</span><span class="o">.</span><span class="n">conn</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"归还连接: </span><span class="si">{</span><span class="bp">self</span><span class="o">.</span><span class="n">conn</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">main</span><span class="p">():</span>
<span class="n">pool</span> <span class="o">=</span> <span class="n">AsyncConnectionPool</span><span class="p">(</span><span class="n">max_size</span><span class="o">=</span><span class="mi">3</span><span class="p">)</span>
<span class="k">await</span> <span class="n">pool</span><span class="o">.</span><span class="n">connect</span><span class="p">()</span>
<span class="k">async</span> <span class="k">with</span> <span class="n">pool</span> <span class="k">as</span> <span class="n">conn</span><span class="p">:</span>
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.5</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"使用 </span><span class="si">{</span><span class="n">conn</span><span class="si">}</span><span class="s2"> 执行查询"</span><span class="p">)</span>
<span class="n">asyncio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
四、异步生成器与流式数据处理
异步生成器使用
1 | async for |
逐个产出数据,非常适合处理流式数据、分页API响应等场景:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 <span class="kn">import</span><span class="w"> </span><span class="nn">asyncio</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">async_range</span><span class="p">(</span><span class="n">n</span><span class="p">:</span> <span class="nb">int</span><span class="p">):</span>
<span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">n</span><span class="p">):</span>
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
<span class="k">yield</span> <span class="n">i</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">paginate_api</span><span class="p">(</span><span class="n">page_size</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">10</span><span class="p">,</span> <span class="n">total_pages</span><span class="p">:</span> <span class="nb">int</span> <span class="o">=</span> <span class="mi">5</span><span class="p">):</span>
<span class="k">for</span> <span class="n">page</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="n">total_pages</span> <span class="o">+</span> <span class="mi">1</span><span class="p">):</span>
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mf">0.3</span><span class="p">)</span>
<span class="n">data</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"item_</span><span class="si">{</span><span class="n">page</span><span class="si">}</span><span class="s2">_</span><span class="si">{</span><span class="n">i</span><span class="si">}</span><span class="s2">"</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="n">page_size</span><span class="p">)]</span>
<span class="k">yield</span> <span class="p">{</span><span class="s2">"page"</span><span class="p">:</span> <span class="n">page</span><span class="p">,</span> <span class="s2">"data"</span><span class="p">:</span> <span class="n">data</span><span class="p">}</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">main</span><span class="p">():</span>
<span class="k">async</span> <span class="k">for</span> <span class="n">num</span> <span class="ow">in</span> <span class="n">async_range</span><span class="p">(</span><span class="mi">5</span><span class="p">):</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"收到数据: </span><span class="si">{</span><span class="n">num</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">async</span> <span class="k">for</span> <span class="n">page_result</span> <span class="ow">in</span> <span class="n">paginate_api</span><span class="p">():</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"第</span><span class="si">{</span><span class="n">page_result</span><span class="p">[</span><span class="s1">'page'</span><span class="p">]</span><span class="si">}</span><span class="s2">页: </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">page_result</span><span class="p">[</span><span class="s1">'data'</span><span class="p">])</span><span class="si">}</span><span class="s2">条记录"</span><span class="p">)</span>
<span class="n">asyncio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">main</span><span class="p">())</span>
五、实战技巧与常见陷阱
在使用asyncio时,有几个重要的实践建议:
1. 避免在协程中使用阻塞操作:如
1 | time.sleep() |
、
1 | requests.get() |
等会阻塞整个事件循环。应使用
1 | aiohttp |
、
1 | aiofiles |
等异步替代库。
2. 使用信号量控制并发数:防止同时打开过多连接导致资源耗尽。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 <span class="kn">import</span><span class="w"> </span><span class="nn">asyncio</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">aiohttp</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">fetch_with_limit</span><span class="p">(</span><span class="n">sem</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">session</span><span class="p">):</span>
<span class="k">async</span> <span class="k">with</span> <span class="n">sem</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"请求: </span><span class="si">{</span><span class="n">url</span><span class="si">}</span><span class="s2">"</span><span class="p">)</span>
<span class="k">async</span> <span class="k">with</span> <span class="n">session</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span> <span class="k">as</span> <span class="n">resp</span><span class="p">:</span>
<span class="k">return</span> <span class="k">await</span> <span class="n">resp</span><span class="o">.</span><span class="n">text</span><span class="p">()</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">main</span><span class="p">():</span>
<span class="n">sem</span> <span class="o">=</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">Semaphore</span><span class="p">(</span><span class="mi">5</span><span class="p">)</span>
<span class="n">urls</span> <span class="o">=</span> <span class="p">[</span><span class="sa">f</span><span class="s2">"https://httpbin.org/delay/</span><span class="si">{</span><span class="n">i</span><span class="w"> </span><span class="o">%</span><span class="w"> </span><span class="mi">3</span><span class="si">}</span><span class="s2">"</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">20</span><span class="p">)]</span>
<span class="k">async</span> <span class="k">with</span> <span class="n">aiohttp</span><span class="o">.</span><span class="n">ClientSession</span><span class="p">()</span> <span class="k">as</span> <span class="n">session</span><span class="p">:</span>
<span class="n">tasks</span> <span class="o">=</span> <span class="p">[</span><span class="n">fetch_with_limit</span><span class="p">(</span><span class="n">sem</span><span class="p">,</span> <span class="n">url</span><span class="p">,</span> <span class="n">session</span><span class="p">)</span> <span class="k">for</span> <span class="n">url</span> <span class="ow">in</span> <span class="n">urls</span><span class="p">]</span>
<span class="n">results</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">gather</span><span class="p">(</span><span class="o">*</span><span class="n">tasks</span><span class="p">,</span> <span class="n">return_exceptions</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
<span class="nb">print</span><span class="p">(</span><span class="sa">f</span><span class="s2">"完成 </span><span class="si">{</span><span class="nb">len</span><span class="p">(</span><span class="n">results</span><span class="p">)</span><span class="si">}</span><span class="s2"> 个请求"</span><span class="p">)</span>
3. 超时控制:使用
1 | asyncio.wait_for() |
或
1 | asyncio.timeout() |
避免协程无限挂起。
1
2
3
4
5
6
7
8
9
10
11 <span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">slow_operation</span><span class="p">():</span>
<span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">100</span><span class="p">)</span>
<span class="k">return</span> <span class="s2">"done"</span>
<span class="k">async</span> <span class="k">def</span><span class="w"> </span><span class="nf">main_timeout</span><span class="p">():</span>
<span class="k">try</span><span class="p">:</span>
<span class="n">result</span> <span class="o">=</span> <span class="k">await</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">wait_for</span><span class="p">(</span><span class="n">slow_operation</span><span class="p">(),</span> <span class="n">timeout</span><span class="o">=</span><span class="mf">3.0</span><span class="p">)</span>
<span class="k">except</span> <span class="n">asyncio</span><span class="o">.</span><span class="n">TimeoutError</span><span class="p">:</span>
<span class="nb">print</span><span class="p">(</span><span class="s2">"操作超时,已取消"</span><span class="p">)</span>
<span class="n">asyncio</span><span class="o">.</span><span class="n">run</span><span class="p">(</span><span class="n">main_timeout</span><span class="p">())</span>
4. 正确处理异常:使用
1 | return_exceptions=True |
或
1 | TaskGroup |
来妥善处理并发任务中的异常,避免单个任务失败导致所有任务被取消。

总结
Python asyncio通过事件循环和协程机制,以同步代码的编写风格实现了高效的异步I/O操作。核心要点包括:使用
1 | async def |
定义协程、
1 | await |
等待异步操作完成、
1 | gather() |
或
1 | TaskGroup |
并发执行任务、信号量控制并发数、以及使用异步上下文管理器管理资源。掌握这些技巧,你就能用Python构建出高性能的异步应用系统。
汤不热吧