Python(2)---并发编程

作者: whenif | 来源:发表于2017-02-09 23:45 被阅读437次

目录

1. Python多线程与多进程知识
  1.1 并发与并行
  1.2 线程(thread)与进程(process)
  1.3 IO密集型与CPU密集型
  1.4 GIL(Global Interpreter Lock)
2. Python多线程与多进程编程实战
  2.1 测试环境
  2.2 导入库并定义浏览器伪装函数
  2.3 定义HTTP访问函数
  2.4 单线程计时函数
  2.5 多线程计时函数
  2.6 多进程计时函数
  2.7 执行效率可视化
  2.8 函数调用
  2.9 结果分析
3. 后记


1 Python多线程与多进程

1.1 并发与并行

** (1)并发 **
  对应多线程,相当于一个人轮流喂两个孩子,看上去好像是喂两个孩子,实际上每次只有一个孩子在吃东西。
  在单处理器上,并发程序虽然有多个上下文运行环境,但某一个时刻只有一个任务在运行;在多处理器上,因为有了多个执行单元,就可以同时有数个任务在跑,这就是为什么多线程可以提高速度的原因之一。
** (2)并行 **
  对应多进程,相当于两个人喂两个孩子,这才是真正意义上的并行。和并发相比,并行更加强调同一时刻有多个任务同时运行。

1.2 线程(thread)与进程(process)

进程好比一个工厂里面的某个车间,线程好比该车间内部的不同工人协同完成任务。教科书上最经典的一句话是:“进程是资源分配的最小单位,线程是CPU调度的最小单位”推荐以下文章进一步阅读。

1.3 IO密集型与CPU密集型

(1)IO密集型任务
  磁盘IO、网络IO占主要的任务,计算量很小。比如请求网页、读写文件等。当然我们在Python中可以利用sleep达到IO密集型任务的目的。
(2)计算密集型任务
  CPU计算占主要的任务,CPU一直处于满负荷状态。比如例如一个计算圆周率至小数点一千位以下的程序,在执行的过程当中绝大部份时间用在三角函数和开根号的计算,复杂的加减乘除等。

1.4 GIL(Global Interpreter Lock)

GIL是为线程安全而生,但随着多核技术的普及其越来越被诟病,很多人经常将GIL和Python无法高效的实现多线程划上等号。
  网络也有很多介绍GIL的文章,在此不再复述,具体可以参考以下文章。

2 Python多线程与多进程编程实战

在Python中实现多线程和多进程主要涉及的库有thread、threading与multiprocessing。前两者主要用来实现多线程,后者用来实现多线程与多进程,在此以multiprocessing为例进行多线程与多进程编程实战爬取豆瓣网。

2.1 测试环境

(1)软硬件:CentOS6.5+Anaconda 4.3.0 For Linux(Python 3.6)
虚拟机配置如下图:

虚拟机配置

(2)测试网站:豆瓣电影

之所以选择Linux环境是因为在windows下开启多进程的时候出现如下情况(程序一直在运行没办法退出),尝试了将Python3.5环境改成2.7环境也是如此,似乎陷入进程死循环中。在此不解,如有简友明白的欢迎简信交流。

2.2 导入库并定义浏览器伪装函数

import urllib
from multiprocessing.dummy import Pool as mulThread #多线程池
from multiprocessing import Pool as mulProcess #多进程池
import time
import random  
import pylab as pl

def GetUserAgent():
   '''
   功能:随机获取HTTP_User_Agent
   '''
   user_agents=[
   "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; AcooBrowser; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
   "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0; Acoo Browser; SLCC1; .NET CLR 2.0.50727; Media Center PC 5.0; .NET CLR 3.0.04506)",
   "Mozilla/4.0 (compatible; MSIE 7.0; AOL 9.5; AOLBuild 4337.35; Windows NT 5.1; .NET CLR 1.1.4322; .NET CLR 2.0.50727)",
   "Mozilla/5.0 (Windows; U; MSIE 9.0; Windows NT 9.0; en-US)",
   "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 2.0.50727; Media Center PC 6.0)",
   "Mozilla/5.0 (compatible; MSIE 8.0; Windows NT 6.0; Trident/4.0; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; .NET CLR 1.0.3705; .NET CLR 1.1.4322)",
   "Mozilla/4.0 (compatible; MSIE 7.0b; Windows NT 5.2; .NET CLR 1.1.4322; .NET CLR 2.0.50727; InfoPath.2; .NET CLR 3.0.04506.30)",
   "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN) AppleWebKit/523.15 (KHTML, like Gecko, Safari/419.3) Arora/0.3 (Change: 287 c9dfb30)",
   "Mozilla/5.0 (X11; U; Linux; en-US) AppleWebKit/527+ (KHTML, like Gecko, Safari/419.3) Arora/0.6",
   "Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.1.2pre) Gecko/20070215 K-Ninja/2.1.1",
   "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9) Gecko/20080705 Firefox/3.0 Kapiko/3.0",
   "Mozilla/5.0 (X11; Linux i686; U;) Gecko/20070322 Kazehakase/0.4.5",
   "Mozilla/5.0 (X11; U; Linux i686; en-US; rv:1.9.0.8) Gecko Fedora/1.9.0.8-1.fc10 Kazehakase/0.5.6",
   "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.56 Safari/535.11",
   "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_3) AppleWebKit/535.20 (KHTML, like Gecko) Chrome/19.0.1036.7 Safari/535.20",
   "Opera/9.80 (Macintosh; Intel Mac OS X 10.6.8; U; fr) Presto/2.9.168 Version/11.52",
   "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/536.11 (KHTML, like Gecko) Chrome/20.0.1132.11 TaoBrowser/2.0 Safari/536.11",
   "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.71 Safari/537.1 LBBROWSER",
   "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; LBBROWSER)",
   "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E; LBBROWSER)",
   "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/535.11 (KHTML, like Gecko) Chrome/17.0.963.84 Safari/535.11 LBBROWSER",
   "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
   "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; QQBrowser/7.0.3698.400)",
   "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
   "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 5.1; Trident/4.0; SV1; QQDownload 732; .NET4.0C; .NET4.0E; 360SE)",
   "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; QQDownload 732; .NET4.0C; .NET4.0E)",
   "Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.1; WOW64; Trident/5.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E)",
   "Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
   "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.1 (KHTML, like Gecko) Chrome/21.0.1180.89 Safari/537.1",
   "Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X; zh-cn) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/5.0.2 Mobile/8C148 Safari/6533.18.5",
   "Mozilla/5.0 (Windows NT 6.1; Win64; x64; rv:2.0b13pre) Gecko/20110307 Firefox/4.0b13pre",
   "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:16.0) Gecko/20100101 Firefox/16.0",
   "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.64 Safari/537.11",
   "Mozilla/5.0 (X11; U; Linux x86_64; zh-CN; rv:1.9.2.10) Gecko/20100922 Ubuntu/10.10 (maverick) Firefox/3.6.10"
   ]
   user_agent = random.choice(user_agents)
   return user_agent

2.3 定义HTTP访问函数

def getURLs():
    '''获取所需爬取的所有URL'''
    urls = []
    for i in range(0, 101,20):#每翻一页其start值增加20
        keyword = "科幻"
        keyword = urllib.request.quote(keyword)
        newpage = "https://movie.douban.com/tag/"+keyword+"?start="+str(i)+"&type=T"
        urls.append(newpage)
    return urls  

def getResponse(url):
    '''获取响应信息'''
    global res
    user_agent = GetUserAgent()
    headers=("User-Agent",user_agent)
    opener = urllib.request.build_opener() 
    opener.addheaders = [headers] 
    try:
        res = opener.open(url,timeout=5).read()
    except Exception as er:
        print("爬取的时候发生错误,具体如下:")
        print(er)
    return res

2.4 单线程计时函数

def singleTime(urls):
    '''单进程计时'''
    time1 = time.time()
    for i in urls:
        print(i)
        getResponse(i) 
    time2 = time.time()
    return str(time2 - time1) 

2.5 多线程计时函数

def mulThreadTime(urls,nums):
    '''多线程计时'''
    pool = mulThread(processes=nums) #开启四个线程
    time3 = time.time()
    pool.map(getResponse,urls)
    pool.close()
    pool.join() #等待线程池中的worker进程执行完毕
    time4 = time.time()
    return str(time4 - time3)  

2.6 多进程计时函数

def mulProcessTime(urls,nums):
    '''多进程计时'''
    pool = mulProcess(processes=nums) #开启四个进程
    time5 = time.time()
    pool.map(getResponse,urls)
    pool.close()
    pool.join() #等待进程池中的worker进程执行完毕
    time6 = time.time()
    return str(time6 - time5) 

2.7 执行效率可视化

def getPlot(singleTimes,mulThreadTimes,mulProcessTimes):
    '''执行效率可视化'''
    thread_num = [2,3,4,5,6,7,8]
    process_num = [2,3,4,5,6,7,8]
    pl.plot(thread_num,[singleTimes]*7,'b',label='singleTimes')
    pl.plot(thread_num,mulThreadTimes,'r',label='mulThreadTimes')
    pl.plot(process_num,mulProcessTimes,'g',label='mulProcessTimes')
    pl.xlabel('The num of thread or process')# make axis labels
    pl.ylabel('times(s)')
    pl.legend()
    show_plot = pl.show()# show the plot on the screen 
    return show_plot

2.8 函数调用

if __name__ == '__main__':
    urls = getURLs()
    ####单线程计时  
    singleTimes = singleTime(urls) 
    ####多线程计时
    mulThreadTimes = []
    for num in range(2,9):
        tmpTimes = mulThreadTime(urls,num)
        mulThreadTimes.append(tmpTimes) 
    ####多进程计时
    mulProcessTimes = []
    for num in range(2,9):
        tmpTimes = mulProcessTime(urls,num)
        mulProcessTimes.append(tmpTimes) 
    ####执行效率可视化
    getPlot(singleTimes,mulThreadTimes,mulProcessTimes)

2.9 结果分析

res.png

(1)多线程与多进程的效率均优于单进程
(2)多线程在3线程时效率最优,5线程效率最差
(3)多进程在7进程时效率最优,5进程效率最差
(4)在多线程在4个线程前效率均大于同等数量的多进程,但到了4个以及4个以线程效率均差于同等数量的多进程

Python的多线程在多核CPU上,只对于IO密集型计算产生正面效果;而当有至少有一个CPU密集型线程存在,那么多线程效率会由于GIL而大幅下降。

3 后记

大致记录了Python并发的相关知识,关于里面的效率呈现机理在后续的学习中有待进一步研究。

参考与拓展阅读:
[1]Python模块学习:threading 多线程控制和处理
[2]Python中的multiprocessing和threading
[3]Python 多线程 threading和multiprocessing模块
[4]Python并发编程之协程/异步IO


个人Github
个人博客whenif
欢迎各路同学互相交流

相关文章

  • 使用 Python 进行并发编程系列 - 收藏集 - 掘金

    使用 Python 进行并发编程 - asyncio 篇 (三) - 掘金 这是「使用Python进行并发编程...

  • Python(2)---并发编程

    目录 1. Python多线程与多进程知识1.1 并发与并行1.2 线程(thread)与进程(process)...

  • Python并行编程(一):线程的基本概念和线程的两种定义方法以

    前言:本系列将包含Python并行编程的相关技术内容,包括Python线程、Python进程、并发编程的异步模式及...

  • python asyncio并发编程(2)

    1. 获取协程任务中的函数返回值 运行结果 2.为协程任务添加不带参数的callback函数 3.为协程任务添加带...

  • 2019实战第二期-时间读书打卡

    2019实战第二期-时间读书打卡 --读《Python编程快速上手—让繁琐工作自动化》 Python里面的并发分2...

  • Python并发编程

    协程 Python社区虽然对于异步编程的支持相比其他语言稍显迟缓,但是也在Python3.4中加入了asyncio...

  • Python 并发编程

    线程 线程调用的两种形式 1 . 直接调用 继承式调用 Thread实例的方法 同步锁 递归锁 递归锁,其中维护一...

  • python并发编程

    1. python 单进程 用下载两个文件模拟单进程的问题。 运行结果 2. python 多进程 多进程可以有效...

  • python并发编程

    一、引子 顾名思义,进程即正在执行的一个过程。进程是对正在运行程序的一个抽象。 进程的概念起源于操作系统,是操作系...

  • python并发编程

    一:多线程 二:多线程的并发运行 三:如何给线程传递参数? 四:通过继承创建线程 五:线程同步和互斥锁 保证线程的...

网友评论

  • 望月成三人:python如果想实现并行,最好使用多进程+协程的方式
    whenif:@望月成三人 :+1:,后续再研究一下

本文标题: Python(2)---并发编程

本文链接:https://www.haomeiwen.com/subject/mvnwittx.html