• Python之并发编程(八)同步和异步两种执行方式


    同步和异步两种执行方式

    1. 进行运行的三种状态:运行、就绪、阻塞

    2. 阻塞、非阻塞、同步、异步

      • 阻塞(程序运行的角度):程序运行时,遇到IO,程序挂起,cpu被切走
      • 非阻塞(程序运行的角度):程序没有遇到IO;程序遇到IO但我通过某种手段,让cpu强行运行我的程序
      • 同步(提交任务的角度):提交一个任务,自任务开始运行直到此任务结束(可能有IO)返回一个返回值之后,我在提交下一个任务(示例:先告知第一个老师完成写书任务,我从原地等待,等他两天之后完成了,告诉完事了,我再发布下一个任务。。。)
      • 异步(提交任务的角度):一次提交多个任务,然后我就执行下一行代码(示例:直接将任务告知三个老师,我就忙我的,直到三个老师完成之后告知我)
        • 异步返回结果如何回收?
    3. 同步调用与异步调用:

      • 异步调用:

        from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
        import time
        import random
        import os
        
        def task(i):
            print(f'{os.getpid()}开始任务')
            time.sleep(random.randint(1,3))
            print(f'{os.getpid()}任务结束')
            return i
        if __name__=='__main__':
            #异步调用
            pool=ProcessPoolExecutor()
            for i in range(10):
                pool.submit(task,i)
                
            pool.shutdown(wait=True)
            #shutdown:让我的主进程等待进程池中所有的子进程都结束任务之后,再执行,有点类似与join
            #shutdown:在上一个进程池没有完成所有的任务之前,不允许添加新的任务
            #一个任务时通过一个函数实现的,任务完成得返回值就是函数得返回值
            print('====主')
        
      • 同步调用:

        from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
        
        import time
        import random
        import os
        
        def task(i):
            print(f'{os.getpid()}开始任务')
            time.sleep(random.randint(1,3))
            print(f'{os.getpid()}任务结束')
            return i
        if __name__ == '__main__':
            pool=ProcessPoolExecutor()
            for i in range(10):
                obj=pool.submit(task,i)
                #obj是一个动态对象,返回当前的而对象状态,有可能运行中,有可能(就绪,堵塞)还有可能是结束了
                obj.result()#必须等到这个任务完成后,返回了结果之后,在执行下一个任务
                print(f'任务结果{obj.result()}')
        
            pool.shutdown(wait=True)
            #shutdown:让我的主进程等待进程池中所有的子进程都结束任务之后,在执行,有点类似join
            #shutdown:在上一个进程池没有完成所有的任务之前,不允许添加新的任务
            #一个任务是通过一个函数实现的,任务完成了,他的返回就是函数的返回值
            print('====主')
        
    4. 异步如何取结果?

      • 第一种方式:统一回收结果

        • 方式一:异步调用,统一回收结果
          
          from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
          import time
          import os
          import random
          
          def task(i):
              print(f'{os.getpid()}任务开始')
              time.sleep(random.randint(1,3))
              print(f'{os.getpid()}任务结束')
              return i
          
          if __name__ == '__main__':
              l1=[]
              pool=ProcessPoolExecutor()
              for i in range(10):
                  obj=pool.submit(task,i)
                  l1.append(obj)
              pool.shutdown(wait=True)
              print(l1)
              for i in l1:
                  print(i.result())
          
              print('全')
              
              #统一回收结果:我不能马上收到任何一个已经完成任务的返回值,我只能等到所有任务结束统一回收
          
      • 第二种方式:一个个回收

        import requests
        
        def task(url):
            """模拟的就是爬取多个源代码,一定有IO操作"""
            ret=requests.get(url)
            if ret.status_code==200:
                return ret.text
            
        def parse(content):
            """模拟对数据进行分析 一般没有IO 如果都有IO用回调函数就没有意义"""
            return len(content)
        if __name__ == '__main__':
            """串行,耗费时间长"""
            ret=task('http://www.baidu.com')
            print(parse(ret))
            ret = task('http://www.JD.com')
            print(parse(ret))
        
    5. 异步调用+回调函数

      • 浏览器工作原理:向服务器发送一个请求,服务端验证你的请求,如果正确,给你的浏览器返回一个文件,浏览器接收到文件,将文件里面的代码渲染成你看到的漂亮美丽的模样

      • 什么叫爬虫?

        1. 利用代码模拟一个浏览器,进行浏览器的工作流程得到一堆源代码
        2. 对源代码进行数据清洗得到我想要的数据
      • pip模块为第三方模块,安装里面requests模块

        • 模拟浏览器

          import requests#导入requests模块
          ret=request.get('http://www.baidu.com')#
          if ret.status_code==200:#如果请求状态是成功
          	print(ret.text)#则把源码打印出来
          
      • 异步调用+回调函数(版本一)

        from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
        import time
        import random
        import os
        import requests
        
        def task(url):
            """模拟的就是爬去多个源代码 一定有IO操作"""
            ret=requests.get(url)
            if ret.status_code==200:
                return ret.text
        
        def parse(content):
            """模拟对数据进行分析 一般没有IO 如果有IO 会回调函数就没有意义"""
            return len(content)
        if __name__ == '__main__':
            """开启线程池,并发并行的执行"""
            url_list = [
                'http://www.baidu.com',
                'http://www.JD.com',
                'http://www.JD.com',
                'http://www.JD.com',
                'http://www.taobao.com',
                'https://www.cnblogs.com/jin-xin/articles/7459977.html',
                'https://www.luffycity.com/',
                'https://www.cnblogs.com/jin-xin/articles/9811379.html',
                'https://www.cnblogs.com/jin-xin/articles/11245654.html',
                'https://www.sina.com.cn/',
        
            ]
            pool=ThreadPoolExecutor(4)
            obj_list=[]
            for url in url_list:
                obj=pool.submit(task,url)
                obj_list.append(obj)
            pool.shutdown(wait=True)
            for res in obj_list:
                print(parse(res.result()))
            print("===主")
        #版本一:
        #1.异步发出10个任务,并发的执行,但是统一的接受所有的任务返回值(效率低,不能实时的获取结果)
        #2.分析结果流程是串行,影响效率
        #for res in obj_list:
        #   print(parse(res.result()))
        
      • 异步调用+回调函数(版本二)

        #版本二:针对版本一的缺点2,让串行变成并发或者并行
        from concurrent.futures import  ProcessPoolExecutor,ThreadPoolExecutor
        import time
        import random
        import os
        import requests
        
        def task(url):
            """模拟的就是爬取多个源代码 一定有IO操作"""
            ret=requests.get(url)
            if ret.status_code==200:
                return parse(ret.text)
        def parse(content):
            return len(content)
        
        if __name__ == '__main__':
            url_list = [
                    'http://www.baidu.com',
                    'http://www.JD.com',
                    'http://www.JD.com',
                    'http://www.JD.com',
                    'http://www.taobao.com',
                    'https://www.cnblogs.com/jin-xin/articles/7459977.html',
                    'https://www.luffycity.com/',
                    'https://www.cnblogs.com/jin-xin/articles/9811379.html',
                    'https://www.cnblogs.com/jin-xin/articles/11245654.html',
                    'https://www.sina.com.cn/',
        
                ]
            pool=ProcessPoolExecutor(4)
            obj_list=[]
            for url in url_list:
                obj=pool.submit(task,url)
                obj_list.append(obj)
            '''
            # 1 在开一个线程进程池,并发并行的处理. 再开一个线程进程池,开销大.
            # 2 将原来的任务扩大,
            版本一:
                线程池设置4个线程, 异步发起10个任务,每个任务是通过网页获取源码, 并发执行,
                最后统一用列表回收10个任务, 串行着分析源码.
            版本二:
                线程池设置4个线程, 异步发起10个任务,每个任务是通过网页获取源码+数据分析, 并发执行,
                最后将所有的结果展示出来.
                耦合性增强了.
                并发执行任务,此任务最好是IO阻塞,才能发挥最大的效果
            '''
            pool.shutdown(wait=True)
            for res in obj_list:
                print(res.result())
        
      • 异步调用+回调函数(版本三)

        • #版本三:
          #基于异步调用回收所有任务结果我要做实时回收结果
          #并发执行任务每个任务只是处理IO阻塞,不能增加新功能
          from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
          import time
          import random
          import os
          import requests
          
          def task(url):
              """模拟就是爬去多个源代码 一定有IO操作"""
              ret=requests.get(url)
              if ret.status_code==200:
                  return ret.text
          
          def parse(obj):
              print(len(obj.result()))
          
          if __name__ == '__main__':
              # 开启线程池,并发并行的执行
              url_list = [
                  'http://www.baidu.com',
                  'http://www.JD.com',
                  'http://www.JD.com',
                  'http://www.JD.com',
                  'http://www.taobao.com',
                  'https://www.cnblogs.com/jin-xin/articles/7459977.html',
                  'https://www.luffycity.com/',
                  'https://www.cnblogs.com/jin-xin/articles/9811379.html',
                  'https://www.cnblogs.com/jin-xin/articles/11245654.html',
                  'https://www.sina.com.cn/',
          
              ]
              pool=ThreadPoolExecutor(4)
              for url in url_list:
                  obj=pool.submit(task,url)
                  obj.add_done_callback(parse)
                  """
                  线程池设置4个线程,异步发起10个任务,每个任务都是通过网页获取源码,并发执行
                  当一个任务完成之后,将parse这个分析代码的任务交由剩余的空闲的线程去执行,你这个线程继续处理其他任务
                  如果进程池+回调:回调函数由主进程去执行
                  如果线程池+回调:回调函数由空闲线程去执行
                  """
          
      • 异步和回调的区别?

        • 异步是站在发布任务的角度
        • 回调:站在接受结果的角度:回调函数 按顺序接受每个任务结果,进行下一步操作
        • 异步处理IO类型 回调处理非IO类型
  • 相关阅读:
    Thrift全面介绍
    ZooKeeper全面介绍
    Redis全面介绍
    第三篇:彻底解决ssh.invoke_shell() 返回的中文问题
    第二篇:ssh.invoke_shell() 切换root出现的新问题
    第一篇:UnicodeDecodeError: 'utf-8' codec can't decode byte 0xc3 in position 0: invalid continuation byte
    pycharm安装 suds模块报错:AttributeError: module 'pip' has no attribute 'main'
    变量赋值理解--Pyton中让两个值互换的方法
    02 Django框架基础(APP的创建访问)
    01 Web框架介绍
  • 原文地址:https://www.cnblogs.com/zhangdadayou/p/11432097.html
Copyright © 2020-2023  润新知