• python学习笔记 day39 线程池


    1. 线程池

    开启线程的成本要比开启进程的成本低,但是也不能任意的开线程,可以使用线程池----使用concurrent的futures模块(futures.ThreadPoolExecutor  导入线程池; futures.ProcessPoolExecutor 导入进程池);

    一般开进程池的数目是 cpu个数+1;

    开线程池的数目是cpu 个数*5;

    开启线程---submit()

    from concurrent import futures
    import time
    import random
    
    # futures.ThreadPoolExecutor-----导入线程池
    # futures.ProcessPoolExecutor---导入进程池
    def func(n):
        time.sleep(random.random())
        print("thread--%s"%n)
    
    thread_pool=futures.ThreadPoolExecutor(5)  # 一般开的线程池,开的线程数是cpu个数*5 但是这里我就开五个线程
    for i in range(10):   # 有10个任务需要执行。开五个线程轮流去执行这10个任务
        thread_pool.submit(func,i)  # thread_pool.submit() 相当于t=Thread(func,i)创建线程 和t.start() 开启线程两步

    运行结果:(执行过程,最多五个线程并发执行)

    而且futures.ThreadPoolExexutor(num)是可以接收线程执行函数的返回值的,等到的对象使用result()方法去获取:

    # 如果想使用futures.ThreadPoolExecutor(num)创建的线程池对象thread_pool,使用thread_pool.submit(func,args)开启线程;
    # 如果线程执行的函数有返回值,submit()是可以拿到返回值ret的,直接使用ret.result()就可以获得:(但是多个线程之间就变同步了,因为每一个线程必须得去执行函数,拿到返回值才会轮到下一个线程执行)
    from concurrent import futures import time import random # futures.ThreadPoolExecutor-----导入线程池 # futures.ProcessPoolExecutor---导入进程池 def func(n): time.sleep(random.random()) print("thread--%s" % n) return n*"*" # 线程执行函数具有返回值 thread_pool=futures.ThreadPoolExecutor(5) # 一般开的线程池,开的线程数是cpu个数*5 但是这里我就开五个线程 for i in range(10): # 有10个任务需要执行。开五个线程轮流去执行这10个任务 ret=thread_pool.submit(func,i) # thread_pool.submit() 相当于t=Thread(func,i)创建线程 和t.start() 开启线程两步 print(ret.result()) # futures.ThreadPoolExecutor(num) 建立线程池对象thread_pool,然后使用thread_pool线程池对象.submit(func,args)方法开启线程 # 如果线程执行的函数具有返回值,则submit()的结果是可以拿到返回值的,直接使用ret.result()方法获取 # 但是这样 多个线程之间执行就变同步了,因为每当执行一个线程就需要result()获取线程执行函数的返回值,就需要把函数执行完,才可以拿到,然后才会轮到下一个线程执行

    运行结果:


     如果既想拿到线程执行函数的返回值,又想多个线程之间异步并发执行:

    # 既想拿到线程执行函数的返回值,又想实现多线程之间的异步并发:
    from concurrent import futures
    import time
    import random
    
    # futures.ThreadPoolExecutor-----导入线程池
    # futures.ProcessPoolExecutor---导入进程池
    def func(n):
        time.sleep(random.random())
        print("thread--%s" % n)
        return n*"*"  # 线程执行函数具有返回值
    
    thread_pool=futures.ThreadPoolExecutor(5)  # 一般开的线程池,开的线程数是cpu个数*5 但是这里我就开五个线程
    ret_lst=[]   # 存放开启线程执行函数的返回值,先同时开启很多个线程,但是并不一定要去执行函数拿到返回值,最后统一获取返回值,这样多个线程仍然是异步并发执行的
    for i in range(10):   # 有10个任务需要执行。开五个线程轮流去执行这10个任务
        ret=thread_pool.submit(func,i)  # thread_pool.submit() 相当于t=Thread(func,i)创建线程 和t.start() 开启线程两步
        # print(ret.result())  # futures.ThreadPoolExecutor(num) 建立线程池对象thread_pool,然后使用thread_pool线程池对象.submit(func,args)方法开启线程
                            # 如果线程执行的函数具有返回值,则submit()的结果是可以拿到返回值的,直接使用ret.result()方法获取
        ret_lst.append(ret)
    [print(ret.result()) for ret in ret_lst]  # 这样多个线程就实现异步并发,之前是一个线程必须去执行函数才能拿到返回值,接着才轮到下一个线程,这样就可以异步开多个线程,先不一定要真的去执行func
                # 这种方法可以实现多个线程异步,而且打印多个线程的返回值,有可能是跟上面线程执行函数打印的结果交叉
                # (因为很有可能有的线程执行的快,结果都要返回了,其他线程可能才去执行函数,做线程内的打印)

    运行结果:

    如果想在所有线程异步执行完函数内代码打印,然后再统一打印线程执行函数的返回值呢(就是打印thread-n 和 打印***分开)可以使用thread_pool.shutdown() 相当于进程池在主进程中p.close() p.join() 是一样的道理(就是不允许往进程池添加任务,然后让主进程等待主线程执行完毕)这里也是一样的

    from concurrent import futures
    import time
    import random
    
    # futures.ThreadPoolExecutor-----导入线程池
    # futures.ProcessPoolExecutor---导入进程池
    def func(n):
        time.sleep(random.random())
        print("thread--%s" % n)
        return n*"*"  # 线程执行函数具有返回值
    
    thread_pool=futures.ThreadPoolExecutor(5)  # 一般开的线程池,开的线程数是cpu个数*5 但是这里我就开五个线程
    ret_lst=[]   # 存放开启线程执行函数的返回值,先同时开启很多个线程,但是并不一定要去执行函数拿到返回值,最后统一获取返回值,这样多个线程仍然是异步并发执行的
    for i in range(10):   # 有10个任务需要执行。开五个线程轮流去执行这10个任务
        ret=thread_pool.submit(func,i)  # thread_pool.submit() 相当于t=Thread(func,i)创建线程 和t.start() 开启线程两步
        ret_lst.append(ret)
    
    thread_pool.shutdown()   # 相当于进程池的p.close() p.join() 等待上面所有线程执行函数结束(异步并发),才会统一打印线程的执行函数的返回值
    [print(ret.result()) for ret in ret_lst]  # 这样多个线程就实现异步并发,之前是一个线程必须去执行函数才能拿到返回值,接着才轮到下一个线程,这样就可以异步开多个线程,先不一定要真的去执行func
                # 这种方法可以实现多个线程异步,而且打印多个线程的返回值,有可能是跟上面线程执行函数打印的结果交叉
                # (因为很有可能有的线程执行的快,结果都要返回了,其他线程可能才去执行函数,做线程内的打印)

    运行结果:


    开启线程----map()函数

    from concurrent import futures
    import time
    import random
    def func(n):
        time.sleep(random.random())
        print("thread--%s"%n)
        return n*"*"   # 使用map()开启线程,即使线程执行函数有返回值,map()也拿不到
    
    thread_pool=futures.ThreadPoolExecutor(5)  # 线程池有五个线程
    thread_pool.map(func,range(10))  # map()函数接收第一个参数是多个线程需要执行的任务,第二个参数是一个可迭代的对象---10个任务,但是五个线程去执行

    运行结果:

    需要注意: map() 得到的多个线程执行任务是异步并发的,但是多个线程执行函数有返回值时,map()是获取不到的;

    2. 回调函数---参数就是多线程执行函数的返回值

    from concurrent import futures
    import time
    import random
    
    def func(n):
        time.sleep(random.random())
        print("thread--%s"%n)
        return n*"*"   # 线程执行函数的返回值传给回调函数
    
    def call_func(args):  # 回调函数的参数接收的是多线程执行函数的返回值
        print(args.result())   # submit()可以拿到多线程执行函数的返回值,但是得使用result()才可以获得
    
    thread_pool=futures.ThreadPoolExecutor(5)  # 线程池开五个线程
    for i in range(10):   # 有10个任务,但是开五个线程轮流执行
        thread_pool.submit(func,i).add_done_callback(call_func)

    运行结果:

    3. 作业--使用futures模块的ThreadPoolExecutor线程池实现网页爬取

    import requests
    from concurrent import futures
    def get_url(url):
        ret=requests.get(url)
        return {"url":url,
                "status_code":ret.status_code,
                "content":ret.text,
                "length":len(ret.text)}
    
    def parser(dic):
        print(dic.result())   # 回调函数的参数dic就是线程执行函数的返回值,但是必须使用result()才可以拿到返回值
    
    url_lst=["http://www.baidu.com","http://www.sougou.com","https://home.cnblogs.com/","https://www.bilibili.com/","http://www.python.org",
             "https://music.163.com/#","https://www.douban.com/","https://www.1point3acres.com/bbs/","https://blog.csdn.net/","https://map.baidu.com/"]
    
    thread_pool=futures.ThreadPoolExecutor(5)  # 线程池开五个线程
    ret_lst=[]  # 存放线程执行get_url 回调函数分析爬取结果,把结果存到列表,最后打印,可以实现多线程之间的异步并发
    for url in url_lst:
        thread_pool.submit(get_url,url).add_done_callback(parser)  # 开启五个线程执行get_url 然后把爬取的结果使用回调函数去执行parser

    运行结果:

    talk is cheap,show me the code
  • 相关阅读:
    PAT 甲级 1051 Pop Sequence (25 分)(模拟栈,较简单)
    PAT 甲级 1050 String Subtraction (20 分) (简单送分,getline(cin,s)的使用)
    PAT 甲级 1049 Counting Ones (30 分)(找规律,较难,想到了一点但没有深入考虑嫌麻烦)***...
    PAT 甲级 1048 Find Coins (25 分)(较简单,开个数组记录一下即可)
    PAT 甲级 1047 Student List for Course (25 分)(cout超时,string scanf printf注意点,字符串哈希反哈希)...
    PAT 甲级 1046 Shortest Distance (20 分)(前缀和,想了一会儿)
    R语言实现金融数据的时间序列分析及建模
    ES6 | class类的基本语法总结
    less使用总结
    umi 的项目中如何修改 favicon
  • 原文地址:https://www.cnblogs.com/xuanxuanlove/p/9795215.html
Copyright © 2020-2023  润新知