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

2017-08-19 10:39:14 +08:00
 Zioyi

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

8109 次点击
所在节点    Python
21 条回复
shawlib
2017-08-19 10:52:37 +08:00
什么系统?
BBCCBB
2017-08-19 10:56:13 +08:00
python2 就用 gevent 咯
Zioyi
2017-08-19 10:56:50 +08:00
windows? 难道说 linux 的 python2 支持?
shawlib
2017-08-19 10:59:26 +08:00
windows 不支持 signal(),我也是刚刚遇到这个问题,还不知道怎么解决
SearchDream
2017-08-19 11:34:38 +08:00
试试 Tornado
NoAnyLove
2017-08-19 12:19:34 +08:00
即使是 Python 3 asyncio 在 Windows 上也是不支持异步文件操作的,我也很绝望啊,明明 ProactorEventLoop 用的 IOCP 是支持文件读写的,完全不知道为啥独裁者大人不开发对应的文件支持功能,搜索了半天也没搜索到结果和原因,全程懵逼状态。。。。。。

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

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

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

话说,既然你的并发依赖的是数据本身提供的锁机制,为什么会觉得瓶颈是因为 Python 线程的竞争?你在 Python 上还做了什么同步机制?
Zioyi
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
2017-08-20 11:53:05 +08:00
@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
2017-08-20 11:53:52 +08:00
@Zioyi 建议你跑一跑代码,对比一下性能,记得反馈最终结果哦
NoAnyLove
2017-08-20 12:05:16 +08:00
@Zioyi Orz,突然反应过来,如果你的线程中不存在其他 I/O 操作,或者其他阻塞操作的话,按照之前的写法,你把 50 线程改成单线程,说不定会更快一些。。。。。。。 因为基本上对数据库的所有操作都放入临界区了。不过你之前既然说过 50 线程跑 14 分钟,单线程跑 40 分钟,那我只能推断你的线程中还存在了其他阻塞操作
Zioyi
2017-08-21 12:22:57 +08:00
没错,我的线程中还存在从文件中读取记录的 I/O 操作,基本代码如下:
```
with open('recors.txt', 'rb') as rf:
Zioyi
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 库,等测试出结果后会反馈出来。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/384121

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX