• 并发编程 协程


    浏览器/爬虫都是socket 客户端

    如何提高并发?  多线程  :IO  多进程:计算

    一.IO多路复用

    作用:检测多个socket 是否发生变化(是否连接成功/是否获取数据) (可读/可写)

    regests 模块

    ret=requests.get('https://www.baidu.com/s?wd=alex')
    #DNS解析,根据域名解析出IP 后面的为搜索关键字

    socket

     1 #socket 发送请求
     2 import  socket
     3 cilent=socket.socket()
     4 cilent.connect(("www.baidui.com",80))
     5 cilent.sendall(b'GET /s?wd=alex HTTP/1.0
    host:www.baidu.com
    
    ')
     6 data=cilent.recv(8096)
     7 lst=[]
     8 while 1:
     9     if not data:
    10         break
    11     lst.append(data)
    12 list=b"".join(lst)
    13 list.decode("utf8")

    b"".join(lst)  使列表结成字符串

    2.多线程解决并发 

     1 import  socket
     2 import requests
     3 def func(key):
     4     cilent=socket.socket()
     5     cilent.connect(("www.baidui.com",80))
     6     cilent.sendall(b'GET /s?wd=%s HTTP/1.0
    host:www.baidu.com
    
    ' % key)
     7     data=cilent.recv(8096)
     8     lst=[]
     9     while 1:
    10         if not data:
    11             break
    12         lst.append(data)
    13     list=b"".join(lst)
    14     list.decode("utf8")
    15 import  threading
    16 l=["zq","wa","wd"]
    17 
    18 for item  in  l:
    19     t1=threading.Thread(target=func,args=(item))
    20     t1.start()

    缺点: 这个需要等待,因为有阻塞,会在客户端connect,recv 时刻需要等待客户端 ,怎样才能不等待呢???

    IO多路复用+socket 实现并发请求 (一个线程100个请求)

    这里用到   client.setblocking(False) 

    1 client = socket.socket()
    2 client.setblocking(False) # 将原来阻塞的位置变成非阻塞(报错)
    3 # 百度创建连接: 阻塞
    4 
    5 try:#需要 避免
    6     client.connect(('www.baidu.com',80)) # 执行了但报错了
    7 except BlockingIOError as e:
    8     pass

    >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    IO多路复用+socket 实现并发请求

     1 import socket
     2 import select
     3 
     4 client1 = socket.socket()
     5 client1.setblocking(False) # 百度创建连接: 非阻塞
     6 
     7 try:
     8     client1.connect(('www.baidu.com',80))
     9 except BlockingIOError as e:
    10     pass
    11 
    12 client2 = socket.socket()
    13 client2.setblocking(False) # 百度创建连接: 非阻塞
    14 try:
    15     client2.connect(('www.sogou.com',80))
    16 except BlockingIOError as e:
    17     pass
    18 
    19 client3 = socket.socket()
    20 client3.setblocking(False) # 百度创建连接: 非阻塞
    21 try:
    22     client3.connect(('www.oldboyedu.com',80))
    23 except BlockingIOError as e:
    24     pass
    25 
    26 socket_list = [client1,client2,client3]#创建连接列表,有三个
    27 conn_list = [client1,client2,client3]#再创建
    28 
    29 while True:
    30     rlist,wlist,elist = select.select(socket_list,conn_list,[],0.005)#rlist
    31     # wlist中表示是否已经和socket建立连接对象,有返回值,可写 返回cilent
    32     #rlist中表示是否已经和socket有返回数据,有返回值,可读 返回cilent
    33     #[] 中 将错误信息返回空列表
    34     #0.005 最大0.005秒检测错误
    35     for sk in wlist:
    36         if sk == client1:
    37             sk.sendall(b'GET /s?wd=alex HTTP/1.0
    host:www.baidu.com
    
    ')
    38         elif sk==client2:
    39             sk.sendall(b'GET /web?query=fdf HTTP/1.0
    host:www.sogou.com
    
    ')
    40         else:
    41             sk.sendall(b'GET /s?wd=alex HTTP/1.0
    host:www.oldboyedu.com
    
    ')
    42         conn_list.remove(sk)
    43     for sk in rlist:
    44         chunk_list = []
    45         while True:
    46             try:
    47                 chunk = sk.recv(8096)
    48                 if not chunk:#
    49                     break
    50                 chunk_list.append(chunk)
    51             except BlockingIOError as e:
    52                 break
    53         body = b''.join(chunk_list)
    54         # print(body.decode('utf-8'))
    55         print('------------>',body)
    56         sk.close()
    57         socket_list.remove(sk)
    58     if not socket_list:#这里指当列表中的cilent被取到时,把他们移出作为判断
    59         #直到把最后都取出
    60         break

    用面向对象做IO多路复用

    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
    host:www.baidu.com
    
    ')
                    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()
    View Code

    3.协程

     概念:

    进程,线程 操作系统中存在

    协程,是由程序员创造出来的,不是一个单独存在的东西

    协程:是为线程,对一个线程进行分片,使得线程在代码块之间来回切换执行,而不是在原来逐行执行

     1 import  greenlet
     2 def func1():
     3     print(11)
     4     f2.switch()
     5     print(22)
     6     f2.switch()
     7 def func2():
     8     print(33)
     9     f1.switch()
    10     print(44)
    11 f1=greenlet.greenlet(func1)
    12 f2=greenlet.greenlet(func2)
    13 f1.switch()
    协程示例

    但是单独的协程没有什么作用

    协程只是单纯的分片,但是用在线程上就可以节省宝贵的时间,

    但是无法判断是否是IO进行分片,这时候就需要  pip3 install gevent 

     1 from gevent import monkey
     2 monkey.patch_all()#以后代码中遇到IO都会自动执行greenlet的switch进行切换
     3 import requests
     4 import gevent
     5 def f1(url):
     6     ret=requests.get(url)
     7     print(url,ret.content)
     8 def f2(url):
     9     ret=requests.get(url)
    10     print(url,ret.content)
    11 def f3(url):
    12     ret=requests.get(url)
    13     print(url,ret.content)
    14 gevent.joinall([
    15     gevent.spawn(f1,'https://www.python.org/'),#协程1
    16     gevent.spawn(f2, 'https://www.yahoo.com/'),
    17     gevent.spawn(f3, 'https://www.github.com/'),
    18 ])
    monkey.patch_all()
    总结:
    1. 什么是协程?
    协程也可以称为“微线程”,就是开发者控制线程执行流程,控制先执行某段代码然后再切换到另外函执行代码...来回切换。

    2. 协程可以提高并发吗?
    协程自己本身无法实现并发(甚至性能会降低)。
    协程+IO切换性能提高。

    3. 进程、线程、协程的区别?

    4. 单线程提供并发:
    - 协程+IO切换:gevent
    - 基于事件循环的异步非阻塞框架:Twisted
    手动实现协程:yield关键字生成器 
     1             def f1():
     2                 print(11)
     3                 yield
     4                 print(22)
     5                 yield
     6                 print(33)
     7 
     8             def f2():
     9                 print(55)
    10                 yield
    11                 print(66)
    12                 yield
    13                 print(77)
    14 
    15             v1 = f1()
    16             v2 = f2()
    17 
    18             next(v1) # v1.send(None)
    19             next(v2) # v1.send(None)
    20             next(v1) # v1.send(None)
    21             next(v2) # v1.send(None)
    22             next(v1) # v1.send(None)
    23             next(v2) # v1.send(None)
    View Code

    总体总结

     1 '''
     2 1.本质上socket
     3 2.如何提高并发 ?
     4 多进程:计算 多线程:io
     5 一.
     6 1.通过客户端协议也可以浏览网页
     7 2.协议 遵循
     8 3.间接可以用requests 模块  或者用 socket 模块来引用
     9 4.cilent.setblocking(False)将原来阻塞的位置变成非阻塞(报错)
    10 报错捕获一下  try  except BlockingIOError as e:
    11 作用:单线程遇到io 不等待
    12 需要检测是否连接
    13 ***  io多路复用作用: 检测socket是否已将发生变化
    14 (是否已经连接成功/是否已经获取数据) (可度/可写)
    15 ***
    16 5. select.select()[] >>报错写入  0.005 >内部最多0.005检测错误
    17 6.已经实现的模块 Twisted  基于事件循环实现的异步非阻塞框架
    18 还用封装实现 (没听好)
    19 7.总结;
    20 1.socket默认阻塞,阻塞体现在
    21  connect recv
    22 2. 如何让socket变成非阻塞
    23  setblocking(False)
    24 3. io多路复用
    25  检测多个socket是否发生变化
    26 4. 系统检测socket是否发生变化,三种模式
    27 select "最多1024个socket;循环检测
    28  poil: (主动查询)不限制监听sockrt个数;循环检测(慢)水平触发
    29 epoil : (举手,自己完成主动通知) 回调方式 边缘触发
    30 Python模块:
    31 select.select 
    32 select.epoll 
    33 
    34 5.提高并发方案:
    35 多进程
    36 多线程
    37 异步非阻塞模块(Twisted)  scrapy框架(单线程完成并发)
    38 5.提高并发方案:
    39 控制好线程个数
    40 生产者消费者模型
    41 6.什么是 异步非阻塞?
    42 -非阻塞 ,不等待
    43 比如创建socket对某个地址进行connect、获取接收数据recv时默认都会等待(连接成功或接收到数据),才执行后续操作。
    44 如果设置setblocking(False),以上两个过程就不再等待,但是会报BlockingIOError的错误,只要捕获即可。
    45 异步:
    46 返回一个函数继续调用,
    47 通知,执行完成后自动执行回调函数局或自动执行某种操作
    48 7.什么是同步阻塞?
    49 阻塞 ,等
    50 同步,按照顺序逐步执行
    51 
    52 不如创建socket对某个地址
    53 
    54 
    55 三.
    56 协程,是由程序员创造出来的不是真实存在的东西
    57 由用户控制
    58 协程,就是微线程,对一个线程进程分片,使得线程在代码块之间来回切换执行,
    59 而不是在原来逐行执行
    60 单纯的协程无用
    61 协程的存在意义:类似于单线程实现并发:遇到IO + 协程的切换可以提高性能
    62 3.1
    63 协程+IO切换>>站起来了  pip3 install gevent
    64 from gevent import monkey
    65 monkey.path_all() #默认将代码遇到的IO
    66 总结:
    67 1.什么是协程?
    68 2.协程可以提高并发么?
    69 3.进程,线程,协程的区别面试题?
    70 4.单线程提供并发
    71 -协程+IO切换 :gevent
    72 -基于事件循环的异步非阻塞框架: Twisted
    73 
    74 也可以通过yield
    75 
    76 以后gevent  Twisted 写代码
    77 三种模式:
    78 select
    79 poil
    80 epoil
    View Code
  • 相关阅读:
    ActionResult,PartialViewResult,EmptyResult,ContentResult
    关于控制器
    「6月雅礼集训 2017 Day8」infection
    「6月雅礼集训 2017 Day7」电报
    「6月雅礼集训 2017 Day7」三明治
    「6月雅礼集训 2017 Day7」回转寿司
    「6月雅礼集训 2017 Day5」学外语
    「6月雅礼集训 2017 Day5」仰望星空
    「6月雅礼集训 2017 Day5」吃干饭
    「6月雅礼集训 2017 Day4」寻找天哥
  • 原文地址:https://www.cnblogs.com/zhangqing979797/p/9642510.html
Copyright © 2020-2023  润新知