zoukankan      html  css  js  c++  java
  • 07.协程

    01.协程

    https://www.cnblogs.com/xiaonq/p/7905347.html#i4

    1.1 什么是协程

    1)协程微线程,纤程,本质是一个单线程
    2)协程能在单线程处理高并发,因为遇到IO自动切换
    线程遇到I/O操作会等待、阻塞,协程遇到I/O会自动切换(剩下的只有CPU操作)
    线程的状态保存在CPU的寄存器和栈里而协程拥有自己的空间,所以无需上下文切换的开销,所以快
    3)为甚么协程能够遇到I/O自动切换
    greenlet是C语言写的一个模块,遇到IO手动切换
    协程有一个gevent模块(封装了greenlet模块),遇到I/O自动切换
    4)协程拥有自己的空间,所以无需上下文切换的开销
    #1.2 协程优缺点

    1.2 协程优缺点

    协程缺点
    1、无法利用多核资源:协程的本质是个单线程,它不能同时将 单个CPU 的多个核用上,协程需要和进程配合才能运行在多CPU上
    2、协程如果阻塞掉,整个程序都阻塞
    协程最大的优点
    1、不仅是处理高并发(单线程下处理高并发)
    2、特别节省资源(协程本质是一个单线程,当然节省资源)
      500日活,用php写需要两百多态机器,但是golang只需要二十多太机器

    1.3 协程遇到I/O切换,那活只谁干的?

    简单说法

    1、协程遇到I/O后自动切换,但是会保持一个socket连接,交给系统内核去处理工作
    2、epoll()就工作内核中,他维护了一个链表,来存放所有的socket连接
    3、当内核处理完成后就会回调一个函数,以socket文件描述符为key,结果为value存放到字典中
    4、此时这个列表还是在内核中,需要将这个字典拷贝到用户空间(用户进程中)

    本质

    1.epoll()中内核则维护一个链表,epoll_wait直接检查链表是不是空就知道是否有文件描述符准备好了。
    2.在内核实现中epoll是根据每个sockfd上面的与设备驱动程序建立起来的回调函数实现的。
    3.某个sockfd上的事件发生时,与它对应的回调函数就会被调用,来把这个sockfd加入链表,其他处于“空闲的”状态的则不会。
    4.epoll上面链表中获取文件描述,这里使用内存映射(mmap)技术,避免了复制大量文件描述符带来的开销
    内存映射(mmap):内存映射文件,是由一个文件到一块内存的映射,将不必再对文件执行I/O操作

    1.4 Python中协程的模块

    1、greenlet:遇到I/O手动切换,是一个C模块
    2、gevent:对greenlet封装,遇到I/O自动切换(借助C语言库greenlet)
    3、asyncio:和gevent一样,也是实现协程的一个模块(python自己实现)
    • https://www.cnblogs.com/xiaonq/p/12870204.html

    02.进程,线程,协程爬取页面

    特点:

    1.进程:启用进程非常浪费资源
    2.线程:线程多,并且在阻塞过程中无法执行其他任务
    3.协程:gevent只用起一个线程,当请求发出去后gevent就不管,永远就只有一个线程工作,谁先回来先处理

    2.1 for循环

    第四:性能最差
    import requests
    url_list = [
        'https://www.baidu.com',
        'http://dig.chouti.com/',
    ]
    
    for url in url_list:
        result = requests.get(url)
        print(result.text)

    2.2 进程池

    缺点:启用进程非常浪费资源

    2.2.1 multiprocessing.Pool

    # -*- coding: utf-8 -*-
    import requests
    from multiprocessing import Pool
    
    def fetch_request(url):
        result = requests.get(url)
        print(result.text)
    
    def call(arg):
        print('-->exec done:',"测试进程池执行后回调功能")
    
    url_list = [
        'https://www.baidu.com',
        'https://www.google.com/',         #google页面会卡住,知道页面超时后这个进程才结束
        'http://dig.chouti.com/',          #chouti页面内容会直接返回,不会等待Google页面的返回
    ]
    
    if __name__ ` '__main__':
        pool = Pool(10)        # 创建线程池
        for url in url_list:
            #用法1 callback作用是指定只有当Foo运行结束后就执行callback调用的函数,父进程调用的callback函数
            pool.apply_async(func=fetch_request, args=(url,),callback=call)
        print('end')
        pool.close()    #关闭pool
        pool.join()     #进程池中进程执行完毕后再关闭,如果注释,那么程序直接关闭。

    2.2.2 ProcessPoolExecutor

    import requests
    from concurrent.futures import ProcessPoolExecutor
    
    def fetch_request(url):
        result = requests.get(url)
        print(result.text)
    
    url_list = [
        'https://www.baidu.com',
        'https://www.google.com/',         #google页面会卡住,知道页面超时后这个进程才结束
        'http://dig.chouti.com/',          #chouti页面内容会直接返回,不会等待Google页面的返回
    ]
    
    if __name__ ` '__main__':
        pool = ProcessPoolExecutor(10)        # 创建线程池
        for url in url_list:
            pool.submit(fetch_request,url)    # 去线程池中获取一个进程,进程去执行fetch_request方法
        pool.shutdown(wait=True)

    2.3 线程池

    缺点: 创建一个新线程将消耗大量的计算资源,并且在阻塞过程中无法执行其他任务。
    例: 比如线程池中10个线程同时去10个url获取数据,当数据还没来时这些线程全部都在等待,不做事。
    import requests
    from concurrent.futures import ThreadPoolExecutor
    
    def fetch_request(url):
        result = requests.get(url)
        print(result.text)
    
    url_list = [
        'https://www.baidu.com',
        'https://www.google.com/',         #google页面会卡住,知道页面超时后这个进程才结束
        'http://dig.chouti.com/',          #chouti页面内容会直接返回,不会等待Google页面的返回
    ]
    
    pool = ThreadPoolExecutor(10)            # 创建一个线程池,最多开10个线程
    for url in url_list:
        pool.submit(fetch_request,url)       # 去线程池中获取一个线程,线程去执行fetch_request方法
    
    pool.shutdown(True)                      # 主线程自己关闭,让子线程自己拿任务执行

    2.4 协程

    特点 :gevent只用起一个线程,当请求发出去后gevent就不管,永远就只有一个线程工作,谁先回来先处理
    import gevent
    
    
    import gevent
    from gevent import monkey
    monkey.patch_all(select=False)  # 注意,这个导包顺序不要变
    import requests
    
    # 这些请求谁先回来就先处理谁
    def fetch_async(method, url, req_kwargs):
        response = requests.request(method=method, url=url, `req_kwargs)
        print(response.url, response.content)
    
    # ##### 发送请求 #####
    gevent.joinall([
        gevent.spawn(fetch_async, method='get', url='https://www.baidu.com/', req_kwargs={}),
        gevent.spawn(fetch_async, method='get', url='https://www.google.com/', req_kwargs={}),
        gevent.spawn(fetch_async, method='get', url='https://github.com/', req_kwargs={}),
    ])
  • 相关阅读:
    WEB前端第四十四课——jQuery框架(二)常用方法
    WEB前端第四十三课——jQuery框架(一)$()函数、添加事件监听
    WEB前端第四十二课——应用案例-瀑布流、递归、代理
    WEB前端第四十一课——简单算法与解析indexof、hash、冒泡排序、递归、JSON
    WEB前端第四十课——正则表达式-RegExp、高级
    WEB前端第三十九课——HTML正则表达式-基础、修饰符、检索模式
    SITE STRUCTURE
    CSS GRID ESSENTIALS
    CSS TYPOGRAPHY
    CSS COLOR
  • 原文地址:https://www.cnblogs.com/xiaoxiamiaichiyu/p/14583754.html
Copyright © 2011-2022 走看看