V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
Zioyi
V2EX  ›  Python

各位老师傅们, python2 支持异步 I/O 编程吗?

  •  
  •   Zioyi · 2017-08-19 10:39:14 +08:00 · 8098 次点击
    这是一个创建于 2442 天前的主题,其中的信息可能已经有所发展或是发生改变。

    在下小白,在网上找了很多异步 I/O 教程,都是关于 python3 的 asyncio。python2 只有 yield、send 吗

    第 1 条附言  ·  2017-08-19 15:18:56 +08:00
    感谢各位老师傅的意见,我再补充一下我的目的吧:
    我现在 200 万条数据要向数据库中插入。当前的做法是开多线程( 50 个),设置数据库锁去做插入,耗时 14 分钟(单线程跑要 40 分钟)。想让速度更快一些,用异步 I/O 编程实现( python2.7.13 )可行吗?
    21 条回复    2017-08-23 15:17:49 +08:00
    shawlib
        1
    shawlib  
       2017-08-19 10:52:37 +08:00
    什么系统?
    BBCCBB
        2
    BBCCBB  
       2017-08-19 10:56:13 +08:00
    python2 就用 gevent 咯
    Zioyi
        3
    Zioyi  
    OP
       2017-08-19 10:56:50 +08:00
    windows? 难道说 linux 的 python2 支持?
    shawlib
        4
    shawlib  
       2017-08-19 10:59:26 +08:00
    windows 不支持 signal(),我也是刚刚遇到这个问题,还不知道怎么解决
    SearchDream
        5
    SearchDream  
       2017-08-19 11:34:38 +08:00 via iPhone
    试试 Tornado
    NoAnyLove
        6
    NoAnyLove  
       2017-08-19 12:19:34 +08:00   ❤️ 1
    即使是 Python 3 asyncio 在 Windows 上也是不支持异步文件操作的,我也很绝望啊,明明 ProactorEventLoop 用的 IOCP 是支持文件读写的,完全不知道为啥独裁者大人不开发对应的文件支持功能,搜索了半天也没搜索到结果和原因,全程懵逼状态。。。。。。

    还是得看你具体什么 I/O 操作,是在什么操作系统上,至少 Windows 上的文件和 socket 是不同的。而且,如果是用 select 也可以被称作异步 I/O,只不过没有用到协程罢了。你的问题有点太宽泛了,不够具体。

    另外,如果你会搜索的话,就会知道,还有个东西叫做 Trollius
    lcdtyph
        7
    lcdtyph  
       2017-08-19 13:10:48 +08:00 via iPhone
    纯异步的话可以试试 pyuv,是基于 libuv 封装的,在 win 上底层是 iocp
    aheadlead
        8
    aheadlead  
       2017-08-19 16:00:42 +08:00
    不试试 executemany 吗?
    jason0916
        9
    jason0916  
       2017-08-19 16:07:42 +08:00
    附议 executemany 啊
    wucao219101
        10
    wucao219101  
       2017-08-19 16:09:45 +08:00
    Twisted
    realpg
        11
    realpg  
       2017-08-19 16:25:07 +08:00
    啥数据库?
    ghostheaven
        12
    ghostheaven  
       2017-08-19 16:31:01 +08:00 via Android
    你确定性能瓶颈在 python 吗
    director
        13
    director  
       2017-08-19 23:20:20 +08:00
    mark
    NoAnyLove
        14
    NoAnyLove  
       2017-08-20 00:04:56 +08:00   ❤️ 1
    那么问题来了,具体是什么数据库?经过什么中间架构进行连接的吗(比如是不是要经过 HTTP 请求对数据库进行操作)?还是直接对数据库进行操作?用的什么 Python 库对数据进行操作?

    如果要高效地对数据库进行异步操作,那么操作数据库的 Python 库也必须是异步的,比如 aiomysql。asyncio 至少需要 3.3 才能支持,如果你坚持使用 Python 2 的话,可以选择 gevent,并且搭配支持 gevent 的异步数据库操作库,比如 ultramysql

    话说,既然你的并发依赖的是数据本身提供的锁机制,为什么会觉得瓶颈是因为 Python 线程的竞争?你在 Python 上还做了什么同步机制?
    Zioyi
        15
    Zioyi  
    OP
       2017-08-20 11:31:37 +08:00
    抱歉没有说明清楚,我使用的是 mysql 数据库,使用的 python 库是 pymysq。我使这里设置锁的原因是:我每一个线程都有一个连接数据库的链接(或者说是保持连接的句柄),如果我不加锁地去跑 50 个线程,会出现(Lock wait timeout exceeded; try restarting transaction)的报错,所以我在这里设置锁去保证同时只有一个线程去对数据库操作:
    ```
    if self.mysql_lock.acquire():
    cursor.execute(current_insert_sql)
    connection.commit()
    self.mysql_lock.release()
    ```
    不知我这种设计是否合理?
    NoAnyLove
        16
    NoAnyLove  
       2017-08-20 11:53:05 +08:00   ❤️ 1
    @Zioyi 没遇到过这种情况,不过感觉(Lock wait timeout exceeded; try restarting transaction)是因为你的并发太高了?不过如果使用了 Lock,同时只有一个线程对数据库读写的话,那么感觉有点跑不够。几个选择:

    1. 把 lock 换成 threading.BoundedSemaphore,然后设置一个合适的并发值(多试几次,找出一个合理的值,记得对报错的情况要 try-except )
    2. 直接引入 gevent, monkey.patch_all(),并发 50 个协程,如果出现同样的错误提示,那么很可能是并发高了,同样可以引入 gevent.lock.BoundedSemaphore 来控制并发数,或者减少协程数目。

    如果出现了 2 这种情况,使用协程和多线程的性能差距应该不会特别大。异步 I/O 的性能高需要有足够高的并发数,如果瓶颈是在并发数上,你还可以试试更换其他操作 MySQL 的 Python 库,比如我前面提到的 ultramysql,主页上写的这个库支持个 gevent,star 数也不低,但是居然找不到官方文档,Orz
    NoAnyLove
        17
    NoAnyLove  
       2017-08-20 11:53:52 +08:00   ❤️ 1
    @Zioyi 建议你跑一跑代码,对比一下性能,记得反馈最终结果哦
    NoAnyLove
        18
    NoAnyLove  
       2017-08-20 12:05:16 +08:00   ❤️ 1
    @Zioyi Orz,突然反应过来,如果你的线程中不存在其他 I/O 操作,或者其他阻塞操作的话,按照之前的写法,你把 50 线程改成单线程,说不定会更快一些。。。。。。。 因为基本上对数据库的所有操作都放入临界区了。不过你之前既然说过 50 线程跑 14 分钟,单线程跑 40 分钟,那我只能推断你的线程中还存在了其他阻塞操作
    Zioyi
        19
    Zioyi  
    OP
       2017-08-21 12:22:57 +08:00
    没错,我的线程中还存在从文件中读取记录的 I/O 操作,基本代码如下:
    ```
    with open('recors.txt', 'rb') as rf:
    Zioyi
        20
    Zioyi  
    OP
       2017-08-21 12:33:51 +08:00
    @NoAnyLove 感谢。没错,我的线程中还存在从文件中读取记录的 I/O 操作,基本代码如下:
    ```
    ...
    with open(records1.txt', 'rb') as rf:
    record = rf.readline()
    while record:
    records.append(record)
    if len(records) == 500:
    currenct_insert_sql = function1(records) # records 列表存五百条 record 后做一次数据库 I/O
    if self.mysql_lock.acquire():
    cursor.execute(current_insert_sql)
    connection.commit()
    self.mysql_lock.release()
    records = []
    record = rf.readline()
    ...
    ```
    现在准备参考你的建议,尝试协程和更换 mysql 库,等测试出结果后会反馈出来。
    whx20202
        21
    whx20202  
       2017-08-23 15:17:49 +08:00
    我个人感觉那些 gevent 库,都是用 I/O 多路复用(系统层面)的原理,让开发通过同步方式进行异步(代码层面)的编程

    python 如果想要真正异步 IO 好像有个 pyaio,当然你的需求协程就能做

    如果有错误欢迎讨论
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   4701 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 09:58 · PVG 17:58 · LAX 02:58 · JFK 05:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.