在某些应用场景下,想要提高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. 利用OpenCV的人脸检测给头像带上圣诞帽

    我们来看下效果 原图: 效果: 原理其实很简单: 采用一张圣诞帽的png图像作为素材, 利用png图像背景是透明的,贴在背景图片上就是戴帽子的效果了. 人脸检测的目的主要是为了确定贴帽子的位置,类似p ...

  2. Unity 简易的UI背景昼夜轮替效果

    在UI背景上实现一个简易的有光影照射的昼夜轮替效果,往往比一个死板的UI背景看起来更加形象生动,比较传统的方式是多图轮流替换的序列帧动画,不过要达到整个UI背景大图的所有地方都产生光影效果,那么务必每 ...

  3. UNIX环境高级编程——线程

    线程包含了表示进程内执行环境必需的信息,其中包括进程中标示线程的线程ID.一组寄存器值.栈.调度优先级和策略.信号屏蔽字.errno变量以及线程私有数据. 进程的所有信息对该进程的所有线程都是共享的, ...

  4. hibernate 关于主键

    本文为北京尚学堂hibernate视频的学习笔记 1在xml中定义单个主键生成策略 1.1通过xml <id name="id" type="long" ...

  5. UNIX环境高级编程——标准I/O库

    对一个进程预定义了三个流,并且这三个流可以自动的被进程使用,它们是:标准输入.标准输出.和标准错误. 标准I/O库提供缓冲的目的是尽可能减少使用read和write的次数. 标准I/O库提供了三种类型 ...

  6. pig读取部分列 (全部列中的少部分列)

    pig流式数据,load数据时,不能读入任意列. 但是,可以从头读,只能连续几列.就是前几列.比如10列数据,可以只读前3列.但不能读第3列: 如:数据testdata [wizad@sr104 lm ...

  7. 【一天一道LeetCode】#64. Minimum Path Sum.md

    一天一道LeetCode 本系列文章已全部上传至我的github,地址:ZeeCoder's Github 欢迎大家关注我的新浪微博,我的新浪微博 欢迎转载,转载请注明出处 (一)题目 Given a ...

  8. UseCase事件流描述规范

    文/fasiondog 整理需求用例的编写规范,分享部分UseCase事件流描述规范.其中,准则5~10.12来自<编写有效用例>([美] Alistair Cockburn 著)一书,其 ...

  9. 14_Android中Service的使用,关于广播接收者的说明

     服务:长期后台运行的没有界面的组件 android应用:什么地方需要用到服务? 天气预报:后台的连接服务器的逻辑,每隔一段时间获取最新的天气信息 股票显示:后台的连接服务器的逻辑,每隔一段时间获 ...

  10. Java关键字之finalize

    Java中提供了finalize方法,在垃圾回收器在进行内存释放时会首先调用finalize,但会有一些误区. 1).对象可能不被垃圾回收. 2).垃圾回收并不等于"析构",fin ...