Python并发编程内容回顾
Python并发编程内容回顾
并发编程小结
目录
• 一、到底什么是线程?什么是进程?
• 二、Python多线程情况下:
• 三、Python多进程的情况下:
• 四、为什么有这把GIL锁?
• 五、Python中线程和进程(GIL锁)
• 六、为什么要创建线程?
• 七、为什么要创建进程?
• 八、进程和线程的区别?
• 九、线程创建的越多越好吗?
• 十、生产者消费者模型解决了什么问题?
• 十一、Lock和RLock的区别?
• 十二、进程和线程以及协程的区别?
• 十三、IO多路复用作用?
• 十四、socket默认是否是阻塞的?阻塞体现在哪里?
• 十五、什么是异步非阻塞?
• 十六、什么是同步阻塞?
• 十七、什么是协程?
• 十八、协程可以提高并发吗?
• 十九、提高并发方案:
• 二十、单线程提供并发
o 20.1 基于IO多路复用+socket非阻塞
o 20.2 协程+IO切换:gevent
o 20.3 基于事件循环的异步
o 20.3.1 方法一
o 20.3.2 方法二
o 20.4 基于事件循环的异步非阻塞框架
进程:
一个程序运行起来,资源集合.
开启进程的过程:
操作系统开辟一个内存空间, 把代码放进去, 去运行代码(需要cpu).
ps:(如果是python的话会把解释器代码也放进去)
特点:
- 进程的内存空间彼此隔离
- 主进程要等待所有的子进程结束(原因是:回收僵尸进程)
multiprocessing重要用法:
- join() 等待子进程结束
- 站在当前进程查看pid
- 查看父进程的pid : os.getppid()
- 查看当前进程的pid: os.getpid()
- 查看子进程的pid: 子进程对象.pid()
- 守护进程:首先是一个进程, 守护到主进程最后一行代码结束.
- 进程的互斥锁,一个进程acquire到了在不release的情况下,其他进程无法acquire
- 比串行好在了是部分代码实现串行
- 主要用在多个进程修改数据混乱的场景
生产者消费者模型(一种思想)
生产者只负责生产,消费者只负责消费,彼此都达到了自己最大的效率.
生产者-->队列(盆)--->消费者
线程
cpu真正的执行单位是线程
python里使用的线程是操作系统的原生线程.
在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程,cpu真正的执行单位是线程.
线程和进程的关系
进程相当于线程的容器
进程 = 各种资源+线程
产生一个线程的过程:
每开启一个进程就会自带一个线程, 也可以通过代码调用操作系统开启线程, 代码执行完了线程结束了.
论述进程和线程
- 进程的周期其实是资源的申请和销毁 资源角度
- 线程的周期其实是代码的运行和结束 执行角度
- 线程的创建速度要快于进程
- 进程和线程的创建速度开启子进程需要申请资源开辟空间 慢
- 开启子线程只是告诉操作系统一个执行方案 快
- 线程共享一个进程的内存资源,进程彼此内存资源隔离
threading 的重要用法:
- 子线程.join() : 等待子线程运行结束.
- 守护线程(了解): 首先是一个线程, 守护的的是主进程的完整周期.
- 线程锁(互斥锁)
- 死锁问题: 互相都拿到了彼此想要往下执行的必需条件,互相都不放手里的锁头.
python的多进程vs多线程
大前提要知道:python的多线程无法利用多核优势实现并行,只能实现并发.
io密集型 适合用python的多线程处理.
计算密集型 适合用python的多进程处理.
进程池线程池:
池的功能限制进程数或线程数.
什么时候限制?
当并发的任务数量远远大于计算机所能承受的范围,即无法一次性开启过多的任务数量 , 我就应该考虑去限制我进程数或线程数,从保证服务器不崩.
协程: 单线程下实现并发
什么样的协程才有意义?
只有遇到io切换才有意义.
优点:
应用程序控制切换要比操作系统控制切换快的多
缺点:
协程跟多线程比
如果一个任务io了没有切换, 其他的任务都要等这个任务io结束
协程跟多进程比
无法利用多核优势.
为什么要有协程( 遇到io切换的协程) ?
协程概念本质是程序员抽象出来的,操作系统根本不知道协程存在,也就说来了一个线程我自己遇到io,自己会在内部切换任务,操作系统跟本发现不了我发生了io,也就是实现了单线程下效率最高.
真实的情况是 多进程下开多线程,多线程下开协程(程序员自己控制的)
一、到底什么是线程?什么是进程?
Python自己没有这玩意,Python中调用的操作系统的线程和进程。
二、Python多线程情况下:
计算密集型操作:效率低,Python内置的一个全局解释器锁,锁的作用就是保证同一时刻一个进程中只有一个线程可以被cpu调度,多线程无法利用多核优势,可以通过多进程方式解决,但是比较浪费资源。
IO操作:效率高
三、Python多进程的情况下:
计算密集型操作:效率高(浪费资源),不得已而为之。
IO操作:效率高(浪费资源)
四、为什么有这把GIL锁?
Python语言的创始人在开发这门语言时,目的快速把语言开发出来,如果加上GIL锁(C语言加锁),切换时按照100条字节指令来进行线程间的切换。
五、Python中线程和进程(GIL锁)
GIL锁,全局解释器锁。用于限制一个进程中同一时刻只有一个线程被cpu调度。
扩展:默认GIL锁在执行100个cpu指令(过期时间)。
查看GIL切换的指令个数
import sys
v1 = sys。getcheckinterval()
print(v1)
六、为什么要创建线程?
由于线程是cpu工作的最小单元,创建线程可以利用多核优势实现并行操作(Java/C#)。
注意:线程是为了工作。
七、为什么要创建进程?
进程和进程之间做数据隔离(Java/C#)。
注意:进程是为了提供环境让线程工作。
八、进程和线程的区别?
进程是资源分配的最小单位,线程是程序执行的最小单位。
进程有自己的独立地址空间,每启动一个进程,系统就会为它分配地址空间,建立数据表来维护代码段,堆栈段和数据段,这种操作非常昂贵。而线程是共享进程中的数据的,使用相同的地址空间,因此CPU切换一个线程的花费远比进程小很多,同时创建一个线程的开销比进程要小很多。
线程之间的通信更方便,同一进程下的线程共享全局变量,静态变量,而进程之间的通信需要以通信的方式(IPC)进行。不过如何处理好同步与互斥是编写多线程程序的难点。
但是多进程程序更健壮,多线程程序只要有一个线程死掉,整个进程也死掉了,而一个进程死掉并不会对另外一个进程造成影响,因为进程有自己独立的地址空间。
也正是由于GIL锁的原因:IO密集型操作可以使用多线程,计算密集型可以使用多进程。
九、线程创建的越多越好吗?
不好。线程之间进行切换时,要做上下文管理。
十、生产者消费者模型解决了什么问题?
不用一直等待的问题。
十一、Lock和RLock的区别?
RLock可以多次加锁。
十二、进程和线程以及协程的区别?
进程是cpu资源分配的最小单元,一个进程中可以有多个线程。
线程是cpu计算的最小单元。
对于Python来说他的进程和线程和其他语言有差异,是有GIL锁。
GIL锁保证一个进程中同一时刻只有一个线程被cpu调度。
注意:IO密集型操作可以使用多线程,计算密集型可以使用多进程。
协程,是由程序员创造出来的一个不是真实存在的东西。
协程,是微线程,对一个线程进程分片,使得线程在代码块之间进行来回切换执行,而不是在原来逐行执行。
十三、IO多路复用作用?
检测多个socket是否已经发生变化(是否已经连接成功/是否已经获取数据)(可读/可写)IO多路复用作用?
检测多个socket是否发生变化。
操作系统检测socket是否发生变化,有三种模式:
• select:最多1024个socket;循环去检测。
• poll:不限制监听socket个数;循环去检测(水平触发)。
• epoll:不限制监听socket个数;回调方式(边缘触发)。
Python模块:
• select.select
• select.epoll
十四、socket默认是否是阻塞的?阻塞体现在哪里?
默认是阻塞,填在等待消息和连接
十五、什么是异步非阻塞?
非阻塞,不等待。比如创建socket对某个地址进行connect、获取接收数据recv时默认都会等待(连接成功或接收到数据),才执行后续操作。
如果设置setblocking(False),以上两个过程就不再等待,但是会报BlockingIOError的错误,只要捕获即可。
异步,通知,执行完成之后自动执行回调函数或自动执行某些操作(通知)。比如做爬虫中向某个地址baidu。com发送请求,当请求执行完成之后自执行回调函数。
十六、什么是同步阻塞?
• 阻塞:等
• 同步:按照顺序逐步执行
十七、什么是协程?
协程也可以称为“微线程”,就是开发者控制线程执行流程,控制先执行某段代码然后再切换到另外函执行代码...来回切换。
十八、协程可以提高并发吗?
协程自己本身无法实现并发(甚至性能会降低)。
协程+IO切换性能提高。
十九、提高并发方案:
• 多进程
• 多线程
• 单线程提供并发
二十、单线程提供并发
20.1 基于IO多路复用+socket非阻塞
实现并发请求(一个线程100个请求)
import socket
# 创建socket
client = socket.socket()
# 将原来阻塞的位置变成非阻塞(报错)
client.setblocking(False)
# 百度创建连接: 阻塞
try:
# 执行了但报错了
client.connect(('www.baidu.com',80))
except BlockingIOError as e:
pass
# 检测到已经连接成功
# 问百度我要什么?
client.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
# 我等着接收百度给我的回复
chunk_list = []
while True:
# 将原来阻塞的位置变成非阻塞(报错)
chunk = client.recv(8096)
if not chunk:
break
chunk_list.append(chunk)
body = b''.join(chunk_list)
print(body.decode('utf-8'))
20.2 协程+IO切换:gevent
from gevent import monkey
# 以后代码中遇到IO都会自动执行greenlet的switch进行切换
monkey.patch_all()
import requests
import gevent
def get_page1(url):
ret = requests.get(url)
print(url,ret.content)
def get_page2(url):
ret = requests.get(url)
print(url,ret.content)
def get_page3(url):
ret = requests.get(url)
print(url,ret.content)
gevent.joinall([
gevent.spawn(get_page1, 'https://www.python.org/'), # 协程1
gevent.spawn(get_page2, 'https://www.yahoo.com/'), # 协程2
gevent.spawn(get_page3, 'https://github.com/'), # 协程3
])
20.3 基于事件循环的异步
执行完某个人物后自动调用我给他的函数,非阻塞
20.3.1 方法一
import socket
import select
# 百度创建连接:非阻塞
client1 = socket.socket()
client1.setblocking(False)
try:
client1.connect(('www.baidu.com', 80))
except BlockingIOError as e:
pass
# 搜狗创建连接:非阻塞
client2 = socket.socket()
client2.setblocking(False)
try:
client2.connect(('www.sogou.com', 80))
except BlockingIOError as e:
pass
# GitHub创建连接:非阻塞
client3 = socket.socket()
client3.setblocking(False)
try:
client3.connect(('www.github.com', 80))
except BlockingIOError as e:
pass
# 创建socket列表:socket_list
socket_list = [client1, client2, client3]
# 创建connect列表:conn_list
conn_list = [client1, client2, client3]
while True:
rlist, wlist, elist = select.select(socket_list, conn_list, [], 0.005)
# rlist中表示已近获取数据的socket对象
# wlist中表示已经连接成功的socket对象
# elist中表示出现错误的socket对象
for sk in wlist:
if sk == client1:
sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
elif sk == client2:
sk.sendall(b'GET /web?query=fdf HTTP/1.0\r\nhost:www.sogou.com\r\n\r\n')
else:
sk.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.oldboyedu.com\r\n\r\n')
conn_list.remove(sk)
for sk in rlist:
chunk_list = []
while True:
try:
chunk = sk.recv(8096)
if not chunk:
break
chunk_list.append(chunk)
except BlockingIOError as e:
break
body = b''.join(chunk_list)
# print(body.decode('utf-8'))
print('------------>', body)
sk.close()
socket_list.remove(sk)
if not socket_list:
break
20.3.2 方法二
import socket
import select
class Req(object):
def __init__(self,sk,func):
self.sock = sk
self.func = func
def fileno(self):
return self.sock.fileno()
class Nb(object):
def __init__(self):
self.conn_list = []
self.socket_list = []
def add(self,url,func):
# 创建socket客户端
client = socket.socket()
# 非阻塞
client.setblocking(False)
try:
# 创建连接
client.connect((url, 80))
# 异常处理
except BlockingIOError as e:
pass
obj = Req(client,func)
# 连接列表
self.conn_list.append(obj)
# socket列表
self.socket_list.append(obj)
def run(self):
while True:
rlist,wlist,elist = select.select(self.socket_list,self.conn_list,[],0.005)
# wlist中表示已经连接成功的req对象
for sk in wlist:
# 发生变换的req对象
sk.sock.sendall(b'GET /s?wd=alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n')
self.conn_list.remove(sk)
for sk in rlist:
chunk_list = []
while True:
try:
chunk = sk.sock.recv(8096)
if not chunk:
break
chunk_list.append(chunk)
except BlockingIOError as e:
break
body = b''.join(chunk_list)
# print(body.decode('utf-8'))
sk.func(body)
sk.sock.close()
self.socket_list.remove(sk)
if not self.socket_list:
break
def baidu_repsonse(body):
print('百度下载结果:',body)
def sogou_repsonse(body):
print('搜狗下载结果:', body)
def github_repsonse(body):
print('GITHUB下载结果:', body)
t1 = Nb()
t1.add('www.baidu.com',baidu_repsonse)
t1.add('www.sogou.com',sogou_repsonse)
t1.add('www.github.com',oldboyedu_repsonse)
t1.run()
20.4 基于事件循环的异步非阻塞框架
如Twisted框架,scrapy框架(单线程完成并发)
Python并发编程内容回顾的更多相关文章
- Python并发编程一(多进程)
1.背景知识(进程.多道技术) 顾名思义,进程即正在执行的一个过程.进程是对正在运行程序的一个抽象. 进程的概念起源于操作系统,是操作系统最核心的概念,也是操作系统提供的最古老也是最重要的抽象概念之一 ...
- Python并发编程之深入理解yield from语法(八)
大家好,并发编程 进入第八篇. 直到上一篇,我们终于迎来了Python并发编程中,最高级.最重要.当然也是最难的知识点--协程. 当你看到这一篇的时候,请确保你对生成器的知识,有一定的了解.当然不了解 ...
- Python并发编程系列之多进程(multiprocessing)
1 引言 本篇博文主要对Python中并发编程中的多进程相关内容展开详细介绍,Python进程主要在multiprocessing模块中,本博文以multiprocessing种Process类为中心 ...
- python并发编程&多线程(二)
前导理论知识见:python并发编程&多线程(一) 一 threading模块介绍 multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性 官网链 ...
- python并发编程&多线程(一)
本篇理论居多,实际操作见: python并发编程&多线程(二) 一 什么是线程 在传统操作系统中,每个进程有一个地址空间,而且默认就有一个控制线程 线程顾名思义,就是一条流水线工作的过程,一 ...
- python并发编程&多进程(二)
前导理论知识见:python并发编程&多进程(一) 一 multiprocessing模块介绍 python中的多线程无法利用多核优势,如果想要充分地使用多核CPU的资源(os.cpu_cou ...
- python并发编程&多进程(一)
本篇理论居多,实际操作见: python并发编程&多进程(二) 一 什么是进程 进程:正在进行的一个过程或者说一个任务.而负责执行任务则是cpu. 举例(单核+多道,实现多个进程的并发执行) ...
- Python并发编程理论篇
Python并发编程理论篇 前言 其实关于Python的并发编程是比较难写的一章,因为涉及到的知识很复杂并且理论偏多,所以在这里我尽量的用一些非常简明的语言来尽可能的将它描述清楚,在学习之前首先要记住 ...
- Python并发编程03 /僵孤进程,孤儿进程、进程互斥锁,进程队列、进程之间的通信
Python并发编程03 /僵孤进程,孤儿进程.进程互斥锁,进程队列.进程之间的通信 目录 Python并发编程03 /僵孤进程,孤儿进程.进程互斥锁,进程队列.进程之间的通信 1. 僵尸进程/孤儿进 ...
随机推荐
- linux php composer安装和使用教程
linux php composer安装和使用教程建议在linux下 下载后 然后再下载到本地 win上最好别用composer下载速度超级慢 或者根本下不动 项目依赖包 ...
- 视觉融合定位github
1. obr_slam有关的非常有用的github链接: https://github.com/Ewenwan/MVision/tree/master/vSLAM/oRB_SLAM2 2. gnss, ...
- RC4算法
RC4算法简介:https://baike.baidu.com/item/RC4%E7%AE%97%E6%B3%95/9686396?fr=aladdin RC4算法java实现: /** * RC4 ...
- ElasticSearch如何更新集群的状态
ElasticSearch如何更新集群的状态 最近发生了很多事情,甚至对自己的技术能力和学习方式产生了怀疑,所以有一段时间没更新文章了,估计以后更新的频率会越来越少,希望有更多的沉淀而不是简单地分享. ...
- vue引入bootstrap、jquery
在进行vue的学习,项目中需要引入bootstrap.jquery的步骤. 一.引入jQuery 在当前项目的目录下(就是package.json),运行命令 cnpm install jquery ...
- [转] linux 查找文本过滤grep正则表达式命令详解用法
grep (global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜索工具,它能使用 ...
- 2019-11-29-C#-通过编程的方法在桌面创建回收站快捷方式
原文:2019-11-29-C#-通过编程的方法在桌面创建回收站快捷方式 title author date CreateTime categories C# 通过编程的方法在桌面创建回收站快捷方式 ...
- 01 .NET CORE 2.2 使用OCELOT -- 简单使用
目前参考两篇文章,已实现基本的ocelot的网关功能. https://www.cnblogs.com/xlxr45/p/11320988.html https://www.jianshu.com/p ...
- ListView 根据 文件路径 或 扩展名 显示系统文件图标
private void 获取文件button1_Click(object sender, EventArgs e) { folderBrowserDialog1 ...
- GeoIP的使用-C语言版
0x00. 简介 GeoIP库可以根据IP地址(支持IPv4 和 IPv6), 定位该IP所在的 洲.经纬度.国家.省市.ASN 等信息. GeoIP目前已经升级到GeoIP2,GeoIP2有两个版本 ...