36-进程池线程池
开多线程实现并发的效率是高的,当用户没有那么多的时候,服务器是可以承受压力的
但是一定要以某种方式来设置并发数,让服务器能够实现稳定的运行,控制服务器的线程数
设置池,往里面放池的数量限制,进程池就是往进程池里放进程数,线程池就是往池里放线程数
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
from multiprocessing import Process
import os
import time
import random
# 线程池和进程池的接口是一样的,那么什么时候用进程池,什么时候用线程池
# 池本质就是开进程和线程,本质没有区别
# 计算密集型,需要用多核的优势时候就要用进程池
# I/O密集型,不需要过多CPU资源的时候就要用线程池
def task(name):
print('name is %s pid is %s'%(name, os.getpid()))
time.sleep(random.randint(1,3))
if __name__ == '__main__':
# 进程池最多装4个进程
# pool = ThreadPoolExecutor(4)
pool = ProcessPoolExecutor(4)
for i in range(10):
# 异步调用,提交完任务以后,不用等任务拿到结果,只负责任务做完
pool.submit(task,'panda %s'%i)
pool.shutdown()
print('主线程')
37-异步调用与回调机制
提交任务的两种方式
1.同步调用
# 1.同步调用:提交完任务后,就等待任务执行完毕,拿到结果,再执行下一行代码
# 同步调用的结果就是串行执行的方式
import time
import random
from concurrent.futures import ThreadPoolExecutor
def la(name):
print('%s is laing' % name)
time.sleep(random.randint(1,3))
res = random.randint(7,13)*"*"
return {'name':name, 'res':res}
def weight(obj):
name = obj['name']
size = obj['res']
print('%s is %s'%(name, size))
if __name__ == '__main__':
pool = ThreadPoolExecutor(10)
res1 = pool.submit(la, 'panda').result()
weight(res1)
res2 = pool.submit(la, 'zombie').result()
weight(res2)
res3 = pool.submit(la, 'boy').result()
weight(res3)
2.异步调用
# 2.异步调用:提交完任务以后不在原地等待任务执行完毕,
import time
import random
from concurrent.futures import ThreadPoolExecutor
def la(name):
print('%s is laing' % name)
time.sleep(random.randint(1,3))
res = random.randint(7,13)*"*"
return {'name':name, 'res':res}
def weight(obj):
obj = obj.result()
name = obj['name']
size = obj['res']
print('%s is %s'%(name, size))
if __name__ == '__main__':
pool = ThreadPoolExecutor(10)
pool.submit(la, 'panda').add_done_callback(weight)
pool.submit(la, 'zombie').add_done_callback(weight)
pool.submit(la, 'boy').add_done_callback(weight)
38-进程池线程池小练习
from threading import Thread
from concurrent.futures import ThreadPoolExecutor
import requests
import time
def get(url):
print('GET %s' % url)
response = requests.get(url)
time.sleep(3)
return {'url':url, 'content':response.text}
def parse(res):
res = res.result()
print('%s parse res is %s' % (res['url'], len(res['content'])))
if __name__ == '__main__':
urls = {
}
pool = ThreadPoolExecutor(2)
for i in urls:
pool.submit(get,i).add_done_callback(parse)
39-协程介绍
单线程下实现并发的效果,指定单线程下的对CPU不断的进行切换计算,就可以实现并发的效果
协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率
40-协程实现与总结
协程就是单线程下实现并发,自己在代码级别实现控制
总结协程特点:
- 必须在只有一个单线程里实现并发
- 修改共享数据不需加锁
- 用户程序里自己保存多个控制流的上下文栈
- 附加:一个协程遇到IO操作自动切换到其它协程(如何实现检测IO,yield、greenlet都无法实现,就用到了gevent模块(select机制))
41-greenlet模块
greenlet封装程度高,可以实现多个程序之间来回切换,无法实现检测I/O后才切换程序
from greenlet import greenlet
def eat(name):
print('%s eat 1' % name)
g2.switch('panda')
print('%s eat 2' % name)
g2.switch()
def play(name):
print('%s play 1' % name)
g1.switch()
print('%s play 2' % name)
g1 = greenlet(eat)
g2 = greenlet(play)
# 切换
g1.switch('panda')
42-gevent模块
本质上就是封装了gevent模块,能够实现检测I/O,检测到I/O就会实现切换的操作
import gevent
import time
from gevent import monkey
# 打补丁,所有的设计IO操作的部分都全部打上补丁,做上标记
monkey.patch_all()
def eat(name):
print('%s eat 1' % name)
time.sleep(3)
print('%s eat 2' % name)
def play(name):
print('%s play 1' % name)
time.sleep(5)
print('%s play 2' % name)
g1 = gevent.spawn(eat, 'panda')
g2 = gevent.spawn(play, 'boy')
g1.join()
g2.join()
43-gevent异步提交任务
使用gevent模块就需要打补丁,一定要记得from gevent import monkey;monkey.patch_all()
import gevent
import time
from gevent import monkey;monkey.patch_all()
# 打补丁,所有的设计IO操作的部分都全部打上补丁,做上标记
def eat(name):
print('%s eat 1' % name)
time.sleep(3)
print('%s eat 2' % name)
def play(name):
print('%s play 1' % name)
time.sleep(5)
print('%s play 2' % name)
g1 = gevent.spawn(eat, 'panda')
g2 = gevent.spawn(play, 'boy')
gevent.joinall([g1,g2])
44-基于gevent模块实现并发的套接字通信
gevent模块是单线程下实现多个IO密集型操作的模块操作方式
服务端
import socket
from gevent import monkey,spawn;monkey.patch_all()
def comm(conn):
while True:
try:
data = conn.recv(1024)
if not data:break
conn.send(data.upper())
except ConnectionResetError:
break
conn.close()
def server(ip,port):
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((ip, port))
server.listen(5)
while True:
conn, addr = server.accept()
spawn(comm, conn)
server.close()
if __name__ == '__main__':
g = spawn(server('127.0.0.1', 8080))
g.join()
客户端
import socket
from threading import Thread,currentThread
def client():
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.connect(('127.0.0.1', 8080))
while True:
client.send(('%s hello'%currentThread().getName()).encode('gbk'))
data = client.recv(1024)
print(data.decode('gbk'))
client.close()
if __name__ == '__main__':
for i in range(500):
t = Thread(target=client)
t.start()
45-IO模型介绍
为什么要介绍IO模型?
协程是单线程下的并发,一个线程就能支持500个以上的并发量
同步调用
提交任务的方式:提交完任务以后,在等待结果,拿到结果以后执行下面的代码
异步调用
提交任务的方式:提交完任务以后,不等待结果,接着就是执行下面的代码,异步通常是要和回调函数一起执行的,
因为异步是不等待函数的结果的,但是始终需要结果,自动触发回调函数
注意:同步不等于阻塞
阻塞:遇到IO以后,如果自己不做处理,操作系统就会重新调用CPU,操作系统解决的就是IO阻塞问题
非阻塞:指在不能立刻得到结果之前也会立刻返回,同时该函数不会阻塞当前线程
blocking IO 阻塞IO
nonblocking IO 非阻塞IO
IO multiplexing IO多路复用
signal driven IO 信号驱动IO
asynchronous IO 异步IO
遇到IO就会阻塞,遇到网络IO回原地阻塞
1.服务端什么样的操作属于IO?
服务端的套接字,accept()就属于阻塞,等待链接;recv()、send()也是IO行为
2.为什么这个IO行为会让用户感觉在等待?
recv经历了两个阶段:1.等待数据的阶段;2.把数据冲操作系统的缓存拷贝数据到应用程序阶段
send经历一个阶段:1.从应用程序拷贝数据到操作系统(数据量大的也会产生阻塞)
46-阻塞IO模型
当用户进程调用了recvfrom这个系统调用,kernel就开始了IO的第一个阶段:准备数据。对于network io来说,很多时候数据在一开始还没有到达(比如,还没有收到一个完整的UDP包),这个时候kernel就要等待足够的数据到来
阻塞IO导致程序不能实现并发的效果
线程池可以保证机器在健康的状态下稳定的运行
47-非阻塞IO模型
数据一旦已经到达本地的缓冲区以后,一定要把本地的缓冲区的数据拷贝给操作系统
非租塞IO的作用就是在等待数据的阶段让应用程序不再等待,而是继续执行后面的代码,一段时间后过来再接收数据
这样的操作叫做轮询
1.非阻塞IO在做其他事的时候会有数据来,但是不能及时响应,导致数据不能及时响应
2.服务端没有任务阻塞,就是一个死循环,对于机器来说压力太大了,这一个线程一直处于就绪状态,CPU给服务端的资源太多了,是无用的轮询方式,导致CPU无用的占用太多
48-多路复用IO模型
多路复用IO模型->IO多路复用->IO事件驱动模型
当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,
当任何一个socket中的数据准备好了,select就会返回。这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
这个图和blocking IO的图其实并没有太大的不同,事实上还更差一些。因为这里需要使用两个系统调用\(select和recvfrom\),
而blocking IO只调用了一个系统调用\(recvfrom\)。但是,用select的优势在于它可以同时处理多个connection。
阻塞阶段经历了2个阶段就拿到数据,IO多路复用经历了3个阶段,多了中间的select阶段
select的性能高是因为可以同时监控多个IO阻塞,如果select检测的IO就一个的话,性能是不如单个的纯IO阻塞的
要基于select实现套接字的并发,肯定是用来实现多个IO阻塞的操作
select监听fd变化的过程分析:
用户进程创建socket对象,拷贝监听的fd到内核空间,每一个fd会对应一张系统文件表,内核空间的fd响应到数据后,
就会发送信号给用户进程数据已到;
用户进程再发送系统调用,比如(accept)将内核空间的数据copy到用户空间,同时作为接受数据端内核空间的数据清除,
这样重新监听时fd再有新的数据又可以响应到了(发送端因为基于TCP协议所以需要收到应答后才会清除)。
该模型的优点:
相比其他模型,使用select() 的事件驱动模型只用单线程(进程)执行,占用资源少,不消耗太多 CPU,同时能够为多客户端提供服务。
如果试图建立一个简单的事件驱动的服务器程序,这个模型有一定的参考价值。
该模型的缺点:
首先select()接口并不是实现“事件驱动”的最好选择。因为当需要探测的句柄值较大时,select()接口本身需要消耗大量时间去轮询各个句柄。
很多操作系统提供了更为高效的接口,如linux提供了epoll,BSD提供了kqueue,Solaris提供了/dev/poll,…。
如果需要实现更高效的服务器程序,类似epoll这样的接口更被推荐。遗憾的是不同的操作系统特供的epoll接口有很大差异,
所以使用类似于epoll的接口实现具有较好跨平台能力的服务器会比较困难。
其次,该模型将事件探测和事件响应夹杂在一起,一旦事件响应的执行体庞大,则对整个模型是灾难性的。
49-异步IO模型
用户进程发起read操作之后,立刻就可以开始去做其它的事。而另一方面,从kernel的角度,当它受到一个asynchronous read之后,首先它会立刻返回,所以不会对用户进程产生任何block。然后,kernel会等待数据准备完成,然后将数据拷贝到用户内存,当这一切都完成之后,kernel会给用户进程发送一个signal,告诉它read操作完成了。
- 路飞学城—Python爬虫实战密训班 第三章
路飞学城—Python爬虫实战密训班 第三章 一.scrapy-redis插件实现简单分布式爬虫 scrapy-redis插件用于将scrapy和redis结合实现简单分布式爬虫: - 定义调度器 - ...
- 路飞学城—Python爬虫实战密训班 第二章
路飞学城—Python爬虫实战密训班 第二章 一.Selenium基础 Selenium是一个第三方模块,可以完全模拟用户在浏览器上操作(相当于在浏览器上点点点). 1.安装 - pip instal ...
- 路飞学城Python爬虫课第一章笔记
前言 原创文章,转载引用务必注明链接.水平有限,如有疏漏,欢迎指正. 之前看阮一峰的博客文章,介绍到路飞学城爬虫课程限免,看了眼内容还不错,就兴冲冲报了名,99块钱满足以下条件会返还并送书送视频. 缴 ...
- 路飞学城-Python开发集训-第3章
学习心得: 通过这一章的作业,使我对正则表达式的使用直接提升了一个level,虽然作业完成的不怎么样,重复代码有点多,但是收获还是非常大的,有点找到写代码的感觉了,遗憾的是,这次作业交过,这次集训就结 ...
- 路飞学城-Python开发集训-第1章
学习体会: 在参加这次集训之前我自己学过一段时间的Python,看过老男孩的免费视频,自我感觉还行,老师写的代码基本上都能看懂,但是实际呢?....今天是集训第一次交作业的时间,突然发现看似简单升级需 ...
- 路飞学城-Python开发集训-第4章
学习心得: 学习笔记: 在python中一个py文件就是一个模块 模块好处: 1.提高可维护性 2.可重用 3.避免函数名和变量名冲突 模块分为三种: 1.内置标准模块(标准库),查看所有自带和第三方 ...
- 路飞学城-Python开发集训-第2章
学习心得: 这章对编码的讲解超级赞,现在对于编码终于有一点认知了,但还没有大彻大悟,还需要更加细心的琢磨一下Alex博客和视频,以前真的是被编码折磨死了,因为编码的问题而浪费的时间很多很多,现在终于感 ...
- 路飞学城-Python开发-第二章
''' 数据结构: menu = { '北京':{ '海淀':{ '五道口':{ 'soho':{}, '网易':{}, 'google':{} }, '中关村':{ '爱奇艺':{}, '汽车之家' ...
- 路飞学城-Python开发-第三章
# 数据结构: # goods = [ # {"name": "电脑", "price": 1999}, # {"name&quo ...
- 路飞学城-Python开发-第一章
# 基础需求: # 让用户输入用户名密码 # 认证成功后显示欢迎信息 # 输错三次后退出程序 username = 'pandaboy' password = ' def Login(username ...
随机推荐
- [poj 3318] Matrix Multiplication (随机化+矩阵)
Description You are given three n × n matrices A, B and C. Does the equation A × B = C hold true? In ...
- 小程序中 wx.navigateTo 页面跳转没有反应?
页面js文件中加入 show: function () {wx.navigateTo({url: ‘/pages/show/show’})} 这个函数 目的在于要做跳转到新的页面,但是你可能会遇到一个 ...
- Java基础学习总结(62)——Java中的流和Socket
按行读入方式: BufferedReader(); 1.以行为读取单位,读取比较方便. 按行读一般都是字符读. BufferedReader和PrintWriter的内存分析图: 数据流: 输入 输出 ...
- 关于java发送email
转载:https://blog.csdn.net/qq_32371887/article/details/72821291 1:使用JavaMail发送邮件 // 1.创建一个程序与邮件服务器会话对象 ...
- CF802G Fake News (easy)
CF802G Fake News (easy) 题意翻译 给定一个字符串询问能否听过删除一些字母使其变为“heidi” 如果可以输出“YES”,不然为“NO” 题目描述 As it's the fir ...
- CF899A Splitting in Teams
CF899A Splitting in Teams 题意翻译 n个数,只有1,2,把它们任意分组,和为3的组最多多少 题目描述 There were nn groups of students whi ...
- Oracle解除表锁定问题
1.肯定是你同时打开了多个操作页面,要记得关闭多个打开的sql窗口. 2.可以变相删除表,再重新创建一张同名的表来解除表被锁住的问题
- Python 对Twitter tweet的元素 (Word, Screen Name, Hash Tag)的词汇多样性分析
CODE: #!/usr/bin/python # -*- coding: utf-8 -*- ''' Created on 2014-7-3 @author: guaguastd @name: tw ...
- MongoDB初探系列之四:MongoDB与Java共舞
因为版本号不同,可能API也有所不同.本次学习用的是3.0版本号. 1.使用的mongodb的jdbc驱动版本号为:mongo-java-driver-3.0.0.jar 2.本节仅仅是简介JDBC操 ...
- Fragment状态保存
这篇博文是对官方API Demo的FragmentRetainInstanceSupport.java的学习.路径在android-sdk-macosx/extras/android/support/ ...