在某些应用场景下,想要提高python的并发能力,可以使用多线程,或者协程。比如网络爬虫,数据库操作等一些IO密集型的操作。下面对比python单线程,多线程和协程在网络爬虫场景下的速度。

一,单线程。

  单线程代

 1 #!/usr/bin/env 
2 # coding:utf8

3 # Author: hz_oracle import MySQLdb
import gevent
import requests
import time class DbHandler(object):
def __init__(self, host, port, user, pwd, dbname):
self.host = host
self.port = port
self.user = user
self.pwd = pwd
self.db = dbname def db_conn(self):
try:
self.conn = MySQLdb.connect(host=self.host, port=self.port, user=self.user, passwd=self.pwd, db=self.db, charset="utf8")
self.cursor = self.conn.cursor()
return 1
except Exception as e:
return 0 def get_urls(self, limitation):
sql = """select pic from picurltable limit %s""" % limitation
urls_list = list()
try:
self.cursor.execute(sql)
fetchresult = self.cursor.fetchall()
for line in fetchresult:
urls_list.append(line[0])
print len(urls_list)
except Exception as e:
print u"数据库查询失败:%s" % e
return []
return urls_list def db_close(self):
self.conn.close() def get_pic(url):
try:
pic_obj = requests.get(url).content
except Exception as e:
print u"图片出错"
return ""
filename = url.split('/')[-2]
file_path = "./picture/" + filename + '.jpg'
fp = file(file_path, 'wb')
fp.write(pic_obj)
fp.close()
return "ok" def main():
start_time = time.time()
db_obj = DbHandler(host='127.0.0.1', port=3306, user='root', pwd='123456', dbname='pic')
db_obj.db_conn()
url_list = db_obj.get_urls(100)
map(get_pic, url_list)
#for url in url_list:
# get_pic(url)
end_time = time.time()
costtime = float(end_time) - float(start_time)
print costtime
print "download END" if __name__ == "__main__":
main()

  运行结果

100
45.1282339096
download END

单线程情况下,下载100张图片花了45秒。

再来看多线程的情况下。

#!/usr/bin/env python
# coding:utf8
# Author: hz_oracle import MySQLdb
import gevent
import requests
import time
import threading
import Queue lock1 = threading.RLock()
url_queue = Queue.Queue()
urls_list = list() class DbHandler(object):
def __init__(self, host, port, user, pwd, dbname):
self.host = host
self.port = port
self.user = user
self.pwd = pwd
self.db = dbname def db_conn(self):
try:
self.conn = MySQLdb.connect(host=self.host, port=self.port, user=self.user, passwd=self.pwd, db=self.db, charset="utf8")
self.cursor = self.conn.cursor()
return 1
except Exception as e:
return 0 def get_urls(self, limitation):
sql = """select pic from picurltable limit %s""" % limitation
try:
self.cursor.execute(sql)
fetchresult = self.cursor.fetchall()
for line in fetchresult:
url_queue.put(line[0])
except Exception as e:
print u"数据库查询失败:%s" % e
return 0
return 1 def db_close(self):
self.conn.close() class MyThread(threading.Thread):
def __init__(self):
super(MyThread, self).__init__() def run(self):
url = url_queue.get()
try:
pic_obj = requests.get(url).content
except Exception as e:
print u"图片出错"
return ""
filename = url.split('/')[-2]
file_path = "./picture/" + filename + '.jpg'
fp = file(file_path, 'wb')
fp.write(pic_obj)
fp.close() def main():
start_time = time.time()
db_obj = DbHandler(host='127.0.0.1', port=3306, user='root', pwd='', dbname='pic')
db_obj.db_conn()
db_obj.get_urls(100)
for i in range(100):
i = MyThread()
i.start()
while True:
if threading.active_count()<=1:
break
end_time = time.time()
costtime = float(end_time) - float(start_time)
print costtime
print "download END" if __name__ == "__main__":
main()

运行结果

15.408192873
download END

启用100个线程发现只要花15秒即可完成任务,100个线程可能不是最优的方案,但较单线程有很明显的提升。接着再来看协程。

协程代码

#!/usr/bin/env python
# coding:utf8
# Author: hz_oracle import MySQLdb
import requests
import time
import threading
import Queue from gevent import monkey; monkey.patch_all()
import gevent class DbHandler(object):
def __init__(self, host, port, user, pwd, dbname):
self.host = host
self.port = port
self.user = user
self.pwd = pwd
self.db = dbname def db_conn(self):
try:
self.conn = MySQLdb.connect(host=self.host, port=self.port, user=self.user, passwd=self.pwd, db=self.db, charset="utf8")
self.cursor = self.conn.cursor()
return 1
except Exception as e:
return 0 def get_urls(self, limitation):
urls_list = list()
sql = """select pic from picurltable limit %s""" % limitation
try:
self.cursor.execute(sql)
fetchresult = self.cursor.fetchall()
for line in fetchresult:
urls_list.append(line[0])
except Exception as e:
print u"数据库查询失败:%s" % e
return []
return urls_list def db_close(self):
self.conn.close() def get_pic(url):
try:
pic_obj = requests.get(url).content
except Exception as e:
print u"图片出错"
return ""
filename = url.split('/')[-2]
file_path = "./picture/" + filename + '.jpg'
fp = file(file_path, 'wb')
fp.write(pic_obj)
fp.close()
return "ok" def main():
start_time = time.time()
db_obj = DbHandler(host='127.0.0.1', port=3306, user='root', pwd='123456', dbname='pic')
db_obj.db_conn()
url_list = db_obj.get_urls(100)
gevent.joinall([gevent.spawn(get_pic,url) for url in url_list]) end_time = time.time()
costtime = float(end_time) - float(start_time)
print costtime
print "download END" if __name__ == "__main__":
main()

运行结果

10.6234440804
download END

使用协程发现只花了10秒多,也就是三种方法中最快的。

总结:

三种方法中,单线程最慢,多线程次之,而协程最快。 不过如果对多线程进行优化,也可能变快,这里不讨论。

python单线程,多线程和协程速度对比的更多相关文章

  1. Python并发编程二(多线程、协程、IO模型)

    1.python并发编程之多线程(理论) 1.1线程概念 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程(流水线的工作需要电源,电源就相当于 ...

  2. python 多进程,多线程,协程

    在我们实际编码中,会遇到一些并行的任务,因为单个任务无法最大限度的使用计算机资源.使用并行任务,可以提高代码效率,最大限度的发挥计算机的性能.python实现并行任务可以有多进程,多线程,协程等方式. ...

  3. Python并发编程——多线程与协程

    Pythpn并发编程--多线程与协程 目录 Pythpn并发编程--多线程与协程 1. 进程与线程 1.1 概念上 1.2 多进程与多线程--同时执行多个任务 2. 并发和并行 3. Python多线 ...

  4. 深入浅析python中的多进程、多线程、协程

    深入浅析python中的多进程.多线程.协程 我们都知道计算机是由硬件和软件组成的.硬件中的CPU是计算机的核心,它承担计算机的所有任务. 操作系统是运行在硬件之上的软件,是计算机的管理者,它负责资源 ...

  5. python并发编程之协程知识点

    由线程遗留下的问题:GIL导致多个线程不能真正的并行,CPython中多个线程不能并行 单线程实现并发:切换+保存状态 第一种方法:使用yield,yield可以保存状态.yield的状态保存与操作系 ...

  6. Cpython解释器下实现并发编程——多进程、多线程、协程、IO模型

    一.背景知识 进程即正在执行的一个过程.进程是对正在运行的程序的一个抽象. 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一.操作系统的其他所有内容都 ...

  7. Python之并发编程-协程

    目录 一.介绍 二. yield.greenlet.gevent介绍 1.yield 2.greenlet 3.gevent 一.介绍 协程:是单线程下的并发,又称微线程,纤程.英文名Coroutin ...

  8. python进阶——进程/线程/协程

    1 python线程 python中Threading模块用于提供线程相关的操作,线程是应用程序中执行的最小单元. #!/usr/bin/env python # -*- coding:utf-8 - ...

  9. 32 python 并发编程之协程

    一 引子 本节的主题是基于单线程来实现并发,即只用一个主线程(很明显可利用的cpu只有一个)情况下实现并发,为此我们需要先回顾下并发的本质:切换+保存状态 cpu正在运行一个任务,会在两种情况下切走去 ...

随机推荐

  1. Android的TabHost组件-android的学习之旅(四十)

    TabHost简介 虽然,官方建议用Fagment取代TabHost,但是我们还是大概的介绍一下.TabHost是一种非常简单的组件,TabHost可以很方便的在窗口放置多个标签页,每一个标签页相当于 ...

  2. Java-IO之BufferedInputStream(缓冲输入流)

    BufferedInputStream是缓冲输入流,继承于FilterInputStream,作用是为另一个输入流添加一些功能,本质上是通过一个内部缓冲数组实现的.例如,在新建某输入流对应的Buffe ...

  3. Fedora 19: How to resize/extend (LVM) partition?

    Enlarge the disk using fdisk fdisk -l (to see the partition layout, typically we're dealing with /de ...

  4. 偏置方差分解Bias-variance Decomposition

    http://blog.csdn.net/pipisorry/article/details/50638749 偏置-方差分解(Bias-Variance Decomposition) 偏置-方差分解 ...

  5. 最近很多人问我:saiku下载不下来

    saiku的前段团队 在UI设计方面很专业.但是兼容性差到让人无语.不知道为什么,是不是不给钱,人家故意黑它. 一直忙别的工作,现在整理一些saiku相关网站,百度不到的,google又要FQ.看个合 ...

  6. UNIX环境高级编程——进程环境

    一.main函数 C程序总是从main函数开始.当内核执行C程序时,在调用main前先调用一个特殊的启动例程.可执行程序文件将此启动例程指定为程序的起始地址--这是由连接编译器设置的,而连接编译器则由 ...

  7. 敏捷测试(1)--TDD概念

    题记 本系列笔记将从测试人员的角度,总结在百度两年来的测试经验,记录一个完整的基于敏捷流程的验收测试全过程,分享在测试过程中的一些知识和经验,以及自己的一些理念.总结自己,也希望对大家有益. 概念 验 ...

  8. Ext JS 5初探(一)

    在开始前,先安装好Sencha Cmd 5.然后输入以下命令创建一个测试用的应用程序: sencha -sdk c:\ext5 generate app TestExt5 C:\TestExt5 想不 ...

  9. LeetCode之“链表”:Partition List

    题目链接 题目要求: Given a linked list and a value x, partition it such that all nodes less than x come befo ...

  10. 蘑菇街Android组件与插件化

    插件化的基石 -- apk动态加载 随着我街业务的蓬勃发展,产品和运营随时上新功能新活动的需求越来越强烈,经常可以听到"有个功能我想周x上,行不行".行么?当然是不行啦,上新功能得 ...