IO多路复用

1 IO多路复用:

    检测多个socket是否发生变化(是否链接成功(可读),或者是否获取到信息(可写))

2.socket服务端默认是在connect,recv,阻塞

3.如何让socket编程不阻塞?

  使用socket模块的setbloking方法,将阻塞的地方全部变成非阻塞,有助于提高单线程并发效率。但是会抛BlockingIOError异常,捕获即可。

4.系统检测socket是否发生了变化有三种方法

  1.select:最多监听1024个socket,循环去检测

  2.poll:不限制监听的次数,循环去检测(水平触发)

  3.epoll:不限制监听的系数,回调方法(边缘触发)

5.提高并发的方案:

  1.多进程:密集型计算

  2.多线程:IO操作

  3.异步非阻塞模块(twisted)基于时间循环的异步非阻塞模块

,scrapy框架(单线程完成并发)

6.什么是异步非阻塞?

  非阻塞:就是不等待socket的setblocking方法

  比如创建socket对某个地址进行connect,获取数据recv时会默认阻塞(链接成功后才会接受到数据),才会执行后边的操作,效率不高,会等待前一个socket执行完了之后才执行后边的socket。

但是设置setblocking(False),connect,和recv就不会等待,但是会报BlockingIOError错,捕获异常即可!

  异步:起通知的作用!(烧水)

  执行完之后会自动执行回调函数,或者自动执行某些功能。比如做爬虫的时候,执行完之后会自动执行回调函数!

7.什么是同步阻塞?

  就是等一个socket执行完之后才会执行下一个socket,按照顺序执行。

单线程解决并发
def get_data(key):
    client=socket.socket()
    client.connect(('www.baidu.com',80))
    client.sendall(b'GET /s?wd=%alex HTTP/1.0\r\nhost:www.baidu.com\r\n\r\n' )
    chunk_list=[]
    while 1:
        chunk=client.recv(8096)
        if not chunk:
            break
        chunk_list.append(chunk)
    boby=b''.join(chunk_list)
    print(boby.decode('utf8'))
key_list=['alex','sb','db']
for item in key_list:
    get_data(item)

 4.单线程的并发:select    setbloking

import socket
import select#检测socket是否发生了变化

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.sougou.com',80)) except BlockingIOError as e : pass

client3
=socket.socket() client3.setblocking(False) try: client3.connect(('www.oldboyedu.com',80)) except BlockingIOError as e : pass

socket_list
=[client1,client2,client3]#检测是否服务端给我返回数据了 可读 coon_list=[client1,client2,client3]#检测是否所有的socket和服务器链接成功了 可写 while 1: rlist,wlist,elist=select.select(socket_list,coon_list,[],0.005) # wlist表示已经和服务端连接成功 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') coon_list.remove(sk)

  for sk in rlist: chunk_list=[] while 1: try: chunk=sk.recv(8096) if not chunk:#chunk接收到的数据为空的话,就表示接收完毕 break chunk_list.append(chunk) except BlockingIOError as e: break boby=b''.join(chunk_list) print('--------->',boby.decode('utf8')) # print('--------->',boby) sk.close() socket_list.remove(sk) if not socket_list: break

高级版单线程并发 
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): 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) 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 oldboyedu_repsonse(body): print('老男孩下载结果:', body) t1 = Nb() t1.add('www.baidu.com',baidu_repsonse) t1.add('www.sogou.com',sogou_repsonse) t1.add('www.oldboyedu.com',oldboyedu_repsonse) t1.run()
 

 协程:

  协程是由开发人员创造出来的一个不真实存在的东西。

  协程也叫微线程,是对一个线程进行分片,让线程在代码之间相互切换执行,不再是像原来一样按照顺序执行!

单个的协程没有什么意义,无法实现并发,甚至性能会降低,它和IO 操作相结合,能大大的提高效率,会执行greenlet的switch方法!

 

初识协程;

pip3 install greenlet#安装greenlet模块
import
greenlet def f1(): print(11) gr2.switch() print(22) gr2.switch() def f2(): print(33) gr1.switch() print(44) gr1=greenlet.greenlet(f1) gr2=greenlet.greenlet(f2) gr1.switch()

 

协程+IO操作:

from gevent import monkey
monkey.patch_all() # 以后代码中遇到IO都会自动执行greenlet的switch进行切换
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
])

 

基于yield实现协程

def f1():
    print(11)
    yield
    print(22)
    yield
    print(33)

def f2():
    print(55)
    yield
    print(66)
    yield
    print(77)

v1 = f1()
v2 = f2()

next(v1) # v1.send(None)
next(v2) # v1.send(None)
next(v1) # v1.send(None)
next(v2) # v1.send(None)
next(v1) # v1.send(None)
next(v2) # v1.send(None)

 重点来啦!

进程,线程,协程之间的区别?

  进程是计算机资源分配的最小单元,主要用来数据的隔离。

  线程是计算机工作的最小单元,一个进程可以有多个线程(默认只有一个),一个应用程序有对个进程。

  他们在其他语言中并没有这个概念,在python中,一般IO操作使用多线程,计算密集型计算使用多进程,这主要是由python的GIL锁造成的。

  GIL锁就是在同一时刻同一进程只有一个线程才能被CPU调度,如果想使用计算机多核的优势,那就使用多进程,这就是为什么计算密集型操作使用多进程,而IO操作操作则不占用CPU。

  开发人员让代码更加的强大,就出现了协程,协程本身是不存在的,由程序员自己创造的,协程可以在函数之间相互切换,本身没有太大的意义,但是和IO切换放在一起就特别厉害了,可以让一个线程分片,进而达到一个线程不会停止,一遇到IO就切换到别处,当IO操作完成之后还会切回来。在python中使用协程会遇到一个叫做greenlet的模块实现协程+IO自动切换的模块是gevent.

 

转载于:https://www.cnblogs.com/wqzn/p/9643172.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值