**

**

这篇博客主要内容为python 中多线程以及多进程的效率对比,以及记录自己在做这个实验中遇到的一些问题以及心得

背景引入:

CPU制造商为了追求CPU效率放弃了在CPU频率上的追求(CPU频率即CPU单位时间内可以完成任务的多少),反而开始把方向转向了多核CPU上。那么如何在多核CPU上充分发挥出多核的优势就成了一个问题。

首先是简单介绍下多线程与多进程:

线程:

是程序执行流的最小单元,是系统独立调度和分配CPU(独立运行)的基本单位。线程(有时被称为轻量级进程)跟进程有些相似,不同的是,所有的线程运行在同一个进程中, 共享相同的运行环境。它们可以想像成是在主进程或“主线程”中并行运行的“迷你进程”。

进程:

是资源分配的基本单位。一个进程包括多个线程。进程(有时被称为重量级进程)是程序的一次 执行。每个进程都有自己的地址空间,内存,数据栈以及其它记录其运行轨迹的辅助数据。操作系 统管理在其上运行的所有进程,并为这些进程公平地分配时间。进程也可以通过 fork 和 spawn 操作 来完成其它的任务。不过各个进程有自己的内存空间,数据栈等,所以只能使用进程间通讯(IPC), 而不能直接共享信息。

说简单点进程就好比QQ,浏览器这些应用程序,而线程就像QQ里和不同的人的聊天窗口或者浏览器中播放的音乐,显示的网页。

区别:

1.线程与资源分配无关,它属于某一个进程,并与进程内的其他线程一起共享进程的资源。

2.每个进程都有自己一套独立的资源(数据),供其内的所有线程共享。

3.不论是大小,开销线程要更“轻量级”

4.一个进程内的线程通信比进程之间的通信更快速,有效。(因为共享变量)

测试机:

物理机

虚拟机操作系统

macOS

Ubuntu16.04

CPU

双核

单核

内存

8G

2G

python在管理多线程使用了GIL

pythonGIL解释:

GIL是实现python解释器时引入的一个概念,像C语言一样,python 的解释器也有很多,常见的有CPython,Psyco,PyPy。 而GIL是实现CPyhon时引入的。

In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native threads from executing Python bytecodes at once. This lock is necessary mainly because CPython’s memory management is not thread-safe. (However, since the GIL exists, other features have grown to depend on the guarantees that it enforces.)

GIL的出现是有历史原因的,对于并行和并发这样的多任务,就是为了提高CPU的使用效率,然而需要注意的是,一个CPU一个时间只能实现一个任务,也就是说一个CPU永远不可能并行的,但是可以借助CPU轮训制度频繁切换任务,完成多任务。

python在刚创立的时候只考虑到了单核CPU,那么考虑到多线程的数据完整性以及状态同步就需要加一把大锁,所以呢,GIL应运而生。

GIL说简单点就是一把大锁,一把有很大权力的锁,一把可以控制CPU的锁,它可以保证一个时间内只能有一个CPU在工作,确保了多线程数据完整性和状态同步,看到这个地方,大家就有疑惑了,那还要多核CPU干什么,都成单核的了。这也确实是GIL局限性的地方。从上面的英文解释中可以看出GIL可

+以保证线程的安全,刚看到这个概念我有点疑惑,难道操作系统自身的线程调度机制不可以吗,通过查找操作系统的资料我发现,操作系统还真的不可以,下面介绍几种调度算法;

1、先到先服务调度算法(FCFS)

根据就绪队列的到达时间来服务,此时就绪队列是一个FIFO队列,先到先服务,后到的线程不能抢占前面正在服务的线程。这种算法的优点是实现简单,缺点也很明显,就是CPU进程区间变化很大时,平均等待时间会变化很大。

2、最短作业优先调度(SJF)

顾名思义,就是CPU进程区间最短的先执行,如果两个进程区间具有同样的长度,那么按照FCFS来调度。

SJF可以是抢占的,也可以是不抢占的。它的平均等待时间优于FCFS。

3、优先级调度

其实上面的SJF算法就是一种特殊的优先级调度,只不过这里的优先级定义更加广泛一些,SJF算法的优先级是按照CPU进程区间长短来定义的,这里的优先级可以是其他的一些定义。

优先级调度可以是抢占的,也可以是非抢占的。

优先级调度的一个主要问题是无穷阻塞(也称为饥饿),如果一个线程的优先级很低,可能需要等待很长的时间才能到这个线程执行,甚至永远不执行,一种解决方法是老化(随着时间的增长,增加线程的优先级)

4、轮转法调度(RR)

轮转法调度专门是为分时系统设计的。它类似于FCFS,但是增加了抢占为了切换线程。定义一个较小的时间单元,称为时间片,通常为10-100ms。为了实现RR算法,将就绪队列保存为FIFO队列,新进程增加到就绪队列队尾,CPU调度程序从就绪队列选择第一个进程,设置定时器在一个时间片之后再中断,再分派这个进程。

如果该进程的CPU区间小于时间片,进程本身就会释放CPU,调度程序继续处理下一个进程,如果当前进程的CPU区间比时间片长,定时器会产生CPU中断,实行上下文切换,然后将此进程放到就绪队列队尾,继续调度就绪队列第一个进程。

可以看出计算机并不知道代码的具体含义,所以如果代码只是使用数据就没有问题,如果代码要改变数据可能会导致再多线程时数据不同步状态不统一,所以GIL这把锁就可以保证线程的安全。正如上文所说,GIL也确实是一个缺陷无法充分体现出多核的优势,那么我们应该避免这个问题呢:

1.用其他解析器

之前也提到了既然GIL只是CPython的产物,那么其他解析器是不是更好呢?没错,像JPython这样的解析器由于实现语言的特性,他们不需要GIL的帮助。然而由于用了Java/C#用于解析器实现,他们也失去了利用社区众多C语言模块有用特性的机会。所以这些解析器也因此一直都比较小众。所以这个方法并不推荐

2.使用多进程代替多线程

用multiprocessing替代Thread

multiprocessing库的出现很大程度上是为了弥补thread库因为GIL而低效的缺陷。它完整的复制了一套thread所提供的接口方便迁移。唯一的不同就是它使用了多进程而不是多线程。每个进程有自己的独立的GIL,因此也不会出现进程之间的GIL争抢。当然multiprocessing也不是万能良药。它的引入会增加程序实现时线程间数据通讯和同步的困难。multiprocessing由于进程之间无法看到对方的数据,只能通过在主线程申明一个Queue,put再get或者用share memory的方法。这个额外的实现成本使得本来就非常痛苦的多线程程序编码,变得更加痛苦了。但是这仍然是我觉得对于初学者来说最友好的方法了。

代码实现

python中关于线程的使用涉及到threading模块,进程使用涉及multiprocessing模块

都只是使用最基本的start,len方法,所以这里不多赘述。

直接上代码:

需要的第三方库

import requests

import time

import threading

from multiprocessing import Process

首先是线性执行CPU密集型函数,IO密集型函数

# 定义CPU密集型函数

def count(x, y):

c = 0

while c < 500000:

c += 1

x += x

y += y

#定义IO密集型函数

def write():

f = open("test.txt", "w")

for x in range(500000):

f.write("testwriten")

f.close()

def read():

f = open("test.txt", "r")

lines = f.readlines()

f.close()

#定义网络请求函数

head_request = {

'User-Agent':'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36'

}

url = "http://www.tieba.com"

def http_request():

try:

res = requests.get(url, headers=head_request)

html = res.text

return {"context": html}

except Exception as e:

return {"error": e}

# CPU密集操作

t = time.time()

for x in range(10):

count(1, 1)

print("cpu函数运行时间:", time.time() - t)

# IO密集操作

t = time.time()

for x in range(10):

write()

read()

print("IO函数运行时间:", time.time() - t)

接下来是多线程并发模拟CPU密集型函数`

# 定义CPU密集型函数

def count(x, y):

c = 0

while c < 500000:

c += 1

x += x

y += y

counts = []

t = time.time()

for x in range(10):

thread = threading.Thread(target=count, args=(1,1))

counts.append(thread)

thread.start()

e = counts.__len__() #这里使用了一个魔术方法,获取counts的长度

while True:

for i in counts:

if not i.is_alive():

e -= 1

if e <= 0:

break # 当所有线程执行完毕后退出

print(time.time() - t)

多进程模拟CPU密集型函数

# 定义CPU密集型函数

def count(x, y):

c = 0

while c < 500000:

c += 1

x += x

y += y

counts = []

t = time.time()

for x in range(10):

process = Process(target=count, args=(1,1))

counts.append(process)

process.start()

e = counts.__len__()

while True:

for i in counts:

if not i.is_alive():

e -= 1

if e <= 0:

break

print(time.time() - t)

物理机

虚拟机线性执行CPU密集型函数

65.29

120

线性执行IO密集型函数

1.76

1.6

多线程并发模拟CPU密集型函数

65.92

132.58

多进程并行模拟CPU密集型函数

38.87

127.9

通过上面的表格进行数据对比,明显发现在多核的情况下,python的多进程要比多线程快得多。那么是为什么呢,这里就要说到python的多线程管理机制,在python3.x中,GIL使用计时器(执行时间达到阈值后,当前线程释放GIL)对线程进行管理,这样对CPU密集型程序更加友好,但依然没有解决GIL导致的同一时间只能执行一个线程的问题,所以效率依然不尽如人意。而且在多核的情况下可能在进程调度轮训的过程中还会产生CPU的竞争会产生更加坏的效果。

#GIL即全局解释器锁

然而对于IO密集型函数或者网络请求函数,多线程就是友好地,因为可以利用函数的挂起空闲时间进行线程的转换,充分利用到了多核CPU的优势。

在单核的情况下,模拟的多进程并行的效率也是优于多线程并发的。

Liunx多线程pthread初探:https://blog.csdn.net/xuanandting/article/details/78842795

python单核运行_python下多核,单核CPU对于并行,并发执行效率的对比-Go语言中文社区...相关推荐

  1. python turtle 椭圆_Python易学就会(五)turtle绘制椭圆与递归-Go语言中文社区

    前两篇文章基本涵盖了turtle的大部分功能,同时也借由对turtle功能的展示,厘清了Python的一些语法特点,以利于新手入门.但是短短几个例子,阐述得还是有限,这里再展开两个知识点,一方面对tu ...

  2. python 图灵 微信 菜谱_python——wxpy模块实现微信尬聊(基于图灵机器人)-Go语言中文社区...

    wxpy(微信机器人)是在itchat基础上开发的微信个人功能服务API,基本可以实现微信各种拓展功能, 支持pip安装,适用2.7以及3.4-3.6的python版本 通过# 导入模块 from w ...

  3. python爬取天气数据山东_Python爬取天气预报数据,并存入到本地EXCEL中-Go语言中文社区...

    近期忙里偷闲,搞了几天python爬虫,基本可以实现常规网络数据的爬取,比如糗事百科.豆瓣影评.NBA数据.股票数据.天气预报等的爬取,整体过程其实比较简单,有一些HTML+CSS+DOM树等知识就很 ...

  4. linux连接wpa wifi密码,Linux环境下使用WIFI模块:使用wpa_supplicant工具配置和连接WIFI-Go语言中文社区...

    使用wpa_supplicant工具配置和连接WIFI 实验版本及下载地址 wpa_supplicant:wpa_supplicant-2.7 链接: [http://w1.fi/wpa_suppli ...

  5. mysql php7安装配置_centos7无网络下安装部署php7.1.33+mysql5.7.28+apache2.4.6-Go语言中文社区...

    centos7无网络下安装部署php7.1.33+mysql5.7.28+apache2.4.6 一. 1.先ping www.baidu.com,root账户下,如果未联网,创建目录,把提前下载好的 ...

  6. linux wc read,Linux 下使用 wc 统计文件夹下所有文件的代码行数(包括子目录)-Go语言中文社区...

    wc 命令用于统计文件内容的行数.单词数.字母数. 但是如果想统计一个目录下所有文件的行数,wc 并没有提供递归统计. 不过,可以结合 find 命令,例如: $ wc -l `find -name ...

  7. linux下的go富集分析,GO富集分析(转载)-Go语言中文社区

    GO富集介绍 每个基因都会对应有一个或多个GO term(也就是GO功能). 富集涉及到两个概念:前景基因和背景基因.前景基因就是你关注的要重点研究的基因集,背景基因就是所有的基因集.比如做两个样本对 ...

  8. python10以内的加减计算器_python学习之实现简单计算器(加减乘除)小学生能力测试-Go语言中文社区...

    1.小学生算术能力测试系统: 题目要求: 设计一个程序,用来实现帮助小学生进行百以内的算术练习,它具有以下功能: 提供10道加.减.乘或除四种基本算术运算的题目: 练习者根据显示的题目输入自己的答案, ...

  9. python逐行运行_python逐行执行

    广告关闭 腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元! 我已经编写了一些代码来尝试执行以下操作 我之前在python代码中检索过的ope ...

最新文章

  1. 元学习(meta learning) 最新进展综述论文,28页pdf
  2. java项目集成mybatis_JAVA应用程序单独集成Mybatis使用Demo
  3. 3.MATLAB界面介绍
  4. 极速生成缩略图,Serverless 支撑赛事转播锁定冬奥亮点
  5. 每个人都应该知道的25个大数据术语 1
  6. 如何进入python程序代码编辑环境_Python怎么打开代码编辑器 来学习吧
  7. python开发web运维工具_Python web 开发工具箱
  8. Nginx HTTP之请求行解析函数ngx_http_parse_request_line
  9. 【Python3网络爬虫开发实战】1.7.2-mitmproxy的安装
  10. 网易身患绝症员工被裁事件背后 年轻一代的辛酸和压力
  11. assert:python断言报错语句
  12. UIImageJPEGRepresentation 使用中存在的问题
  13. 在ubuntu用wine安装微信 并解决无法输入文字的问题 ubuntu完美安装微信
  14. Notes Twenty-third days-渗透攻击-红队-红队自研
  15. 计算机网络ping超时,ping请求超时怎么回事?ping请求超时的解决方法
  16. 华为数通笔记-BGP选路与负载分担
  17. Robot framework模拟打开浏览器问题
  18. 计算机一级考试模拟题2003word,2015计算机一级MsOffice练习:Word2003
  19. 编译安装zabbix时遇到configure: error: no acceptable C compiler found in $PATH 问题解决
  20. python 仪表数字识别,利用Python进行数字识别

热门文章

  1. android fragment横屏,Fragment横竖屏
  2. Mac 消除系统更新小红点
  3. 人工智能原理(书籍推荐)
  4. 魔兽7.0服务器维护时间,关于魔兽7.0版本你一定要知道的60条注意事项
  5. ECCV2020-DETR笔记
  6. linux 内存容量换算,Hi3516A开发--内存换算
  7. Win10分屏HDMI检测不到显示器
  8. dvm 与jvm 区别
  9. 人工智能各学派简介:符号主义,连接主义,行为主义
  10. Introductory Combinatorics 5th Solutions Chapter1 1~6