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类型

猜你喜欢

转载自www.cnblogs.com/zhangdadayou/p/11432097.html
今日推荐