You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

asyncio仅单任务运行:多SFTP连接文件上传异常排查

问题排查与解决方案

核心原因分析

你遇到的问题本质是协程没有真正让出CPU控制权,导致事件循环无法切换到其他协程,常见原因有两个:

1. 使用了同步SFTP库(如paramiko)

如果你的代码里用的是paramiko这类同步SFTP客户端,哪怕把它放到asyncio协程里调用,它的write等IO操作都是阻塞式的同步调用——这些操作不会触发asyncio的协程切换,会一直占着事件循环线程,直到整个操作完成。这就导致其他协程根本没机会运行。

2. SHA256计算是同步CPU密集操作

hashlib的update方法是纯CPU操作,属于同步阻塞任务。asyncio是单线程事件循环,一旦某个协程在执行CPU密集任务,会一直占用线程,不会主动让出控制权,其他协程只能等这个任务做完才能运行。

针对性解决方法

替换为异步SFTP库

放弃同步的paramiko,改用支持asyncio的异步SFTP库,比如asyncssh。它的所有IO操作都是原生异步的,调用await方法时会自动让出事件循环,让其他协程有机会执行。示例代码片段:

import asyncio
import asyncssh
import hashlib

async def upload_chunk(sftp, file_path, chunk, digester):
    # 异步SFTP写入,会触发协程切换
    await sftp.write(file_path, chunk, append=True)
    # 将SHA256计算放到线程池避免阻塞事件循环
    await asyncio.get_event_loop().run_in_executor(None, digester.update, chunk)

async def main():
    async with asyncssh.connect('sftp.example.com', username='user', password='pass') as conn:
        async with conn.start_sftp_client() as sftp:
            # 开启多个独立SFTP连接(或复用连接的多个协程)
            tasks = []
            digester = hashlib.sha256()
            with open('large_file.bin', 'rb') as f:
                while chunk := f.read(1024*1024):
                    tasks.append(upload_chunk(sftp, '/remote/path/file.bin', chunk, digester))
            await asyncio.gather(*tasks)
            print(f"SHA256: {digester.hexdigest()}")

# Python 3.6需用以下方式启动
loop = asyncio.get_event_loop()
loop.run_until_complete(main())

把CPU密集的SHA256计算放到线程池

如果必须保留现有SFTP库(或无法切换),或者SHA256计算是瓶颈,需要用run_in_executor把CPU密集任务放到线程池执行,避免阻塞事件循环:

# 替换同步的digester.update(chunk)为:
await asyncio.get_event_loop().run_in_executor(None, digester.update, chunk)

这样SHA256计算会在后台线程执行,当前协程会让出控制权,事件循环可以调度其他协程。

确认事件循环的正确运行

确保你是用asyncio.gather(*tasks)来同时运行多个upload协程,而不是串行等待每个协程完成。比如:

  • 错误写法(串行执行):
for task in tasks:
    await task
  • 正确写法(并行调度):
await asyncio.gather(*tasks)

额外注意事项

  • Python 3.6的asyncio没有asyncio.run(),需要用loop = asyncio.get_event_loop(); loop.run_until_complete(main())代替。
  • 多个SFTP连接要注意服务器的连接数限制,避免被服务器封禁。

内容的提问来源于stack exchange,提问作者Mikhail T.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 12:54:18