zoukankan      html  css  js  c++  java
  • 多线程(三)

    多线程(三)

    1.同步/异步and阻塞/非阻塞

    进程运行的三种状态:运行,就绪,阻塞

    从进程执行的态度:

    阻塞:程序运行时,遇到了IO,程序挂起,cpu被切走

    非阻塞:程序没有遇到IO,或者程序遇到IO,通过某种手段,让cpu强行运行该程序

    从提交任务的角度:

    同步:提交一个任务,自任务开始运行直到此任务结束(可能有IO),返回一个返回值之后,再提交下一个任务

    异步:一次提交多个任务,然后就直接执行下一行代码

    ps:
    1. 同步与异步针对的是函数/任务的调用方式:同步就是当一个进程发起一个函数(任务)调用的时候,一直等到函数(任务)完成,而进程继续处于激活状态。而异步情况下是当一个进程发起一个函数(任务)调用的时候,不会等函数返回,而是继续往下执行当,函数返回的时候通过状态、通知、事件等方式通知进程任务完成。
    
    2. 阻塞与非阻塞针对的是进程或线程:阻塞是当请求不能满足的时候就将进程挂起,而非阻塞则不会阻塞当前进程
    
    3.阻塞调用是指调用结果返回之前,当前线程会被挂起(如遇到io操作)。函数只有在得到结果之后才会将阻塞的线程激活。有人也许会把阻塞调用和同步调用等同起来,实际上他是不同的。对于同步调用来说,很多时候当前线程还是激活的,只是从逻辑上当前函数没有返回而已。
    
    #举例:
    1. 同步调用:apply一个累计1亿次的任务,该调用会一直等待,直到任务返回结果为止,但并未阻塞住(即便是被抢走cpu的执行权限,那也是处于就绪态);
    2. 阻塞调用:当socket工作在阻塞模式的时候,如果没有数据的情况下调用recv函数,则当前线程就会被挂起,直到有数据为止。
    

    2.同步调用/异步调用

    同步调用
    
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    import time,random,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(4) #同步调用
        for i in range(10):
            obj = pool.submit(task,i)
           #obj是一个动态对象,返回的当前对象的状态,可能运行,可能阻塞或结束 
            print(f'任务结果:{obj.result()}')
            #obj.result() 必须等到此任务完成后,得到返回结果,在执行下一个
        pool.shutdown(wait = True)
        #shutdown:主进程等待进程池中的所有子进程结束任务之后,在执行,类似于join
        #wait=True:在上一个进程池没有完成所有的任务之前,不允许添加新的任务
        #一个任务是通过一个函数实现的,任务完成了它的返回值就是函数的返回值
        print('===主')
        
    
    异步调用
    
    1.版本1
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    import time,random,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(4)  # 异步调用
        for i in range(10):
            pool.submit(task,i)
        pool.shutdown(wait =True)
        print('==='主)
        
    异步调用,返回值如何接受?这一问题未解决
    
    2.异步调用接收结果
    异步调用,统一回收结果
        
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    import time,random,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(4)
        l1 = []
        for i in range(10):
            obj = pool.submit(task,i)
            l1.append(obj)
        pool.shutdown(wait=True)
        for i in l1:
            print(i.result())
        print("===主")
        
    统一回收结果:不能马上收到任何一个已经完成的任务的返回值,只能等所有的任务全部结束后统一回收
    
            
    

    3.异步调用+回调函数

    浏览器工作原理:向服务端发送一个请求,服务端验证请求,如果正确,返回一个文件,浏览器接收到文件,将文件里面的代码渲染成可视化页面
        
    爬虫:
    1.利用代码模拟一个浏览器,进行浏览器的工作流程得到网页源代码
    2.对源代码进行数据清洗得到想要的数据
    
    版本1:
        
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    import tim,random,os,requests
    
    def task(url):
        ret = requests.get(url) #模拟爬取多个源代码,一定有IO阻塞
        if ret.status_code = 200:
            return ret.text
        
    def parse(content):
        return len(content) #模拟对数据进行分析,一般没有IO
    
    if __name__ == '__main__':
        url_list = ['http://www.baidu.com',
                   'http://www.JD.com',
                   'http://www.taobao.com'
                   'http://www.sina.com',
                   'http://www.gitee.com',
                   'http://www.zhihu.com',
                   'http://www.7k7k.com',
                   'http://www.JD.com',
                   'http://www.hao123.com',
                   'http://www.4399.com'
                   ]
        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()))   #串行,耗费时间长
            
    版本1:
        1.线程池设置4个线程,异步发出10个任务,每个任务是通过网页获取源码,并发的执行,但是统一的接收所有任务的返回值.(效率低,不能实时的获取结果)
        2.分析结果流程是串行,影响效率
        
    版本2:
        
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    import tim,random,os,requests
    
    def task(url):
        ret = requests.get(url) #模拟爬取多个源代码,一定有IO阻塞
        if ret.status_code = 200:
            return parse(ret.text)
        
    def parse(content):
        return len(content) #模拟对数据进行分析,一般没有IO
    
    if __name__ == '__main__':
        url_list = ['http://www.baidu.com',
                   'http://www.JD.com',
                   'http://www.taobao.com'
                   'http://www.sina.com',
                   'http://www.gitee.com',
                   'http://www.zhihu.com',
                   'http://www.7k7k.com',
                   'http://www.JD.com',
                   'http://www.hao123.com',
                   'http://www.4399.com'
                   ]
        
        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(res.result())
        
    版本2:
        1.线程池设置4个线程,异步发起10个任务,每个任务是通过网页获取源码+数据分析,并发的执行,最后将所有结果展示出来
        2.耦合性增强了,并发执行任务,此任务最好是IO阻塞,才能发挥最大的效果
        
        
        
    版本3:
        基于异步调用,回收所有任务结果:实现实时回收结果,并发执行每个任务只是处理IO阻塞的,不能增加新的功能.
        异步调用+回调函数
    
    from concurrent.futures import ProcessPoolExecutor,ThreadPoolExecutor
    import tim,random,os,requests
    
    def task(url):
        ret = requests.get(url) #模拟爬取多个源代码,一定有IO阻塞
        if ret.status_code = 200:
            return ret.text
        
    def parse(obj):
        print(len(obj.result())) #模拟对数据进行分析,一般没有IO
    
    if __name__ == '__main__':
        url_list = ['http://www.baidu.com',
                   'http://www.JD.com',
                   'http://www.taobao.com'
                   'http://www.sina.com',
                   'http://www.gitee.com',
                   'http://www.zhihu.com',
                   'http://www.7k7k.com',
                   'http://www.JD.com',
                   'http://www.hao123.com',
                   'http://www.4399.com'
                   ]
        
        pool = ThreadPoolExecutor(4)
        for url in url_list:
            obj = pool.submit(task,url)
     		obj.add_done_callback(parse)
            
    版本3:
        线程池设置4个线程,异步发起10个任务,每个任务是通过网页获取源码,并发执行,当一个任务完成之后,将parse这个分析代码的任务交由剩余的空闲的线程去执行,此线程继续去处理其他任务
        如果进程池+回调,回调函数由主进程去执行
        如果线程池+回调,回调函数由空闲的线程去执行
        
    异步与回调:
        异步是站在发布任务的角度
        站在接收结果的角度:回调函数(按顺序接收每个任务的结果,进行下一步处理)
            
    异步+回调:
        异步处理的IO类型
        回调处理非IO类型
    
  • 相关阅读:
    WebApi系列~通过HttpClient来调用Web Api接口~续~实体参数的传递 【转】
    Web API的发布问题
    WCF、WebAPI、WCFREST、WebService之间的区别【转】
    讲给普通人听的分布式数据存储【转载】
    c#开发的程序安装时动态指定windows服务名称
    动态调用WCF服务
    服务端增加WCF服务全局异常处理机制
    生成窗口最大数组
    用栈来求解汉诺塔问题
    使用MockMvc测试Spring mvc Controller
  • 原文地址:https://www.cnblogs.com/tutougold/p/11415196.html
Copyright © 2011-2022 走看看