为何python子线程会等待很长时间
在线博彩导航
在线博彩导航
当前位置 : 在线博彩导航 > 博彩导航网

为何python子线程会等待很长时间

背景:运行一个爬虫,开了10个线程,每个线程先去爬取指定数量的代理作为自己的代理池,然后开始工作。

问题:下面是爬虫日志的两行,可以看到在第一行任务处等待了45秒,而这里不过是输出一条信息,十分不理解为什么等了这么长时间?日志中像这样动辄十几秒什么一两分钟的情形基本都发生在爬取代理的过程中,是否意味着这个任务的代码有问题?

15:57:50    INFO    Thread-2    the proxy already in list, skip
15:58:35    INFO    Thread-10       {https: 117.170.28.178:8123} download 2111 bytes in 0.75 secondsaverage in 1 tries, need 10, available count: 7    

思考:我理解python的多线程调度机制是完成了一条指令后,就可以调用其他线程了,并不是一定要等着这个指令得到了预期的结果,那么如果我的代码写的有问题也不至于影响他的调度吧。这个线程没有进展又不将CPU的使用权让渡出来,GIL为什么不剥夺这个线程的运行时间,总不至于是在等待某个程序块或者函数运行完毕吧。

发现描述的问题主要是对sqlite的不当使用引起的,之前的设计是开启一个连接,直到完成代理池内所有代理的验证,并抓取到一定数量的代理后再关闭连接,且每当有代理信息的加入、修改、删除都去写数据文件,所以导致粗粒度的sqlite长时间处于加锁状态。

发现此问题后做了优化,起始新建连接读取完库存代理后马上关闭连接,之后所有的代理新增、更新、删除数据都暂存在类变量中,直到获取了所有需要的代理后,开启一个新连接,用executemany更新数据,然后关闭连接,完成预定任务,速度就上去了。

不过还是不能理解为什么原来的情形下,线程调度机制会允许那个因为数据库阻塞的线程一直占着资源,而不是及时切换呢?

1

因为有一个家伙占着数据库的坑不拉屎,或者是拉完屎不提裤子,别的兄弟肯定要在坑外排队在线干等,哪怕是每个人都准备好手纸就差脱裤子蹲坑了也没办法。对应的,有几种种优化方法,一是治好便秘快进快出,二是多挖坑,三是大家都就地解决,完事后派个铲屎官把现场打扫干净统一扔进坑里,看来你用的是第三种办法

ferstar · 2016年09月01日

展开评论

所以你的线程是阻塞在写入数据库这一关, 既然你用到的是sqlite那么就再送你一道洪荒之力, 加速数据库写入操作:

import sqlite3

...
conn = sqlite3.connectxxx.db
cur = conn.cursor
cur.execute"CREATE TABLE xxx"  # 建个表
cur.execute"PRAGMA synchronous = OFF"  # 关闭磁盘同步
cur.execute"BEGIN TRANSACTION"  # 开始事务处理
cur.executemany"INSERT INTO names VALUES ?,?", lst  # 批量插入爬到的数据
conn.commit
conn.close
...

用到了三个加速sqlite写入速度的方法

  1. 关闭磁盘同步

  2. SQLite 事务

  3. executemany 批量插入

PS: 另外, 如果内存宽裕, 完全可以把数据库文件扔到tmpfs目录, 这样就会大大消除磁盘I/O带来的影响相当于直接在内存中写入

栏目列表

广告位

在线博彩导航