python 创建和使用线程池

众所周知,python3多线程有threading,很好的支持了多线程,那么问题来了,为什么还需要线程池呢,其实很好回答,如果你要爬取网站有八百页,每页设置一个线程,难道能开启八百个么,光切换的时间也很高了吧。这时候就需要用到线程池,可以设置一个20的线程池,同时只有20个线程在运行,剩下的排队。直接上讲解

线程池模块

在threading中是没有线程池相关功能的,想要运行线程池需要自己重写,很明显像我这么懒不可能重写,而且自己编写线程池很难写的比较完美,还需要考虑复杂情况下的线程同步,很容易发生死锁。所以就用到了这个模块,

from concurrent.futures import ThreadPoolExecutor

从Python3.2开始,标准库为我们提供了concurrent.futures模块,它提供了ThreadPoolExecutor和ProcessPoolExecutor两个类,实现了对threading和multiprocessing的进一步抽象(线程池和进程池的唯一区别就是线程与进程,使用方法,内置函数等都完全一样),可以帮我们自动调度线程,省下大量时间,废话少说,直接上代码:

def thread_text(i,y):
    time.sleep(i+y)
    return i+y
if __name__ == '__main__':
    executor = ThreadPoolExecutor(max_workers=5)
    executor1 = executor.submit(thread_text, 1,1)
    executor2 = executor.submit(thread_text, 2,1)
    executor.shutdown()

首先需要实例化,然后在实例化的同时设置最大线程数,然后使用submit提交线程就ok啦,而且与threading.Thread 的线程不同的是,线程池不阻塞,立即执行,也就是说,不需要start来启动它,

  • 知识点一:线程池不阻塞,不需要start

而且想必有些同学已经看出我这个例子的常规例子的不同了,那就是我传了两个参数,这里也是为了区别threading.Thread的参数,众所周知,threading模块对方法传入参数是以元组的形式,比如我上面写的方法,如果用threading调用的话应该这么写threading.Thread(target=thread_text,args=(1,1)),而线程池则不是,如果翻过源码会发现ThreadPoolExecutor没有固定参数,只有*args和**kwargs,所以,线程池如果要传入多个参数,只需要按照参数位置跟在后面即可。

  • 知识点二:线程池传参不要元组,接着往后写,有多少写多少

在介绍一下它的几个其他函数:
Future 提供了如下方法:

  • cancel():取消该 Future 代表的线程任务。如果该任务正在执行,不可取消,则该方法返回 False;否则,程序会取消该任务,并返回 True。
  • cancelled():返回 Future 代表的线程任务是否被成功取消。
    running():如果该 Future 代表的线程任务正在执行、不可被取消,该方法返回 True。
  • done():如果该 Funture 代表的线程任务被成功取消或执行完成,则该方法返回 True。
  • result(timeout=None):获取该 Future 代表的线程任务最后返回的结果。如果 Future 代表的线程任务还未完成,该方法将会阻塞当前线程,其中 timeout 参数指定最多阻塞多少秒。
  • exception(timeout=None):获取该 Future 代表的线程任务所引发的异常。如果该任务成功完成,没有异常,则该方法返回 None。
  • add_done_callback(fn):为该 Future 代表的线程任务注册一个“回调函数”,当该任务成功完成时,程序会自动触发该 fn 函数。
  • as_completed:上面虽然提供了判断任务是否结束的方法,但是不能在主线程中一直判断啊。有时候我们是得知某个任务结束了,就去获取结果,而不是一直判断每个任务有没有结束。这是就可以使用as_completed方法一次取出所有任务的结果。
from concurrent.futures import ThreadPoolExecutor,as_completed
def thread_text(i,y):
    time.sleep(i+y)
    return i+y
if __name__ == '__main__':
    executor = ThreadPoolExecutor(max_workers=5)
    executor1 = executor.submit(thread_text, 1,1)
    executor2 = executor.submit(thread_text, 2,1)
    for i in as_completed([executor1,executor2]):
        print(i.result())
    executor.shutdown()

as_completed()方法是一个生成器,在没有任务完成的时候,会阻塞,在有某个任务完成的时候,会yield这个任务,就能执行for循环下面的语句,然后继续阻塞住,循环到所有的任务结束。从结果也可以看出,先完成的任务会先通知主线程。

map

除了submit,ThreadPoolExecutor还提供了map函数来添加线程,与常规的map类似,区别在于线程池的 map() 函数会为 iterables 的每个元素启动一个线程,以并发方式来执行 func 函数. 同时,使用map函数,还会自动获取返回值,相当于这样,executor.submit(func).result(),
看例子:

from concurrent.futures import ThreadPoolExecutor,as_completed
def thread_text(i,y):
    time.sleep(i+y)
    return i+y
if __name__ == '__main__':
    executor = ThreadPoolExecutor(max_workers=5)
    li2 = [1,1,1,1,1]
    for i in executor.map(thread_text, range(1, 5), li2):
        print(i)
    print('-------')
    executor.shutdown()

同时,输出顺序和列表的顺序相同,从代码可以看出,使用map函数使得代码更简洁,所以常规使用建议直接使用map。

补充
  1. 不管使用map还是submit,如果子线程发生异常,都会等所有线程执行结束后返回异常,即。线程池以后的代码是不会执行的,因为在线程池结束的瞬间就报错退出了。
  2. map和submit的区别是,submit可以使用exception()方法来捕获异常,使之不会影响到主程序,而map则无法捕获,只能异常退出。submit捕获异常示例如下:
from concurrent.futures import ThreadPoolExecutor,as_completed
def thread_text(i,y):
    time.sleep(i+y)
    return i+y
if __name__ == '__main__':
    executor = ThreadPoolExecutor(max_workers=5)
    executor1 = executor.submit(thread_text, 1,1)
    executor2 = executor.submit(thread_text, 2,1)
    for i in as_completed([executor1,executor2]):
        if i.exception():
            print(i.exception)
        else:
            print(i.result())
    executor.shutdown()

另外,线程锁依然是用threading模块的,线程池这里并没有另外设置锁相关内容。

参考文档
[python] ThreadPoolExecutor线程池
Python线程池及其原理和使用(超级详细)

  • 4
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Python函数中使用线程池有两种方式。第一种方式是使用第三方库threadpool,它提供了一个ThreadPool类来管理线程池。您可以通过创建ThreadPool对象,并使用makeRequests方法创建任务,然后将任务添加到线程池中执行。最后,您可以使用wait方法等待所有任务完成。此外,您还可以定义回调函数来获取任务的结果。 另一种方式是使用Python 3中引入的concurrent.futures.ThreadPoolExecutor库。您可以创建ThreadPoolExecutor对象,并使用submit方法提交任务给线程池。每个任务会返回一个Future对象,您可以使用这个对象的result方法获取任务的结果。 下面是使用这两种方式的示例代码: ```python # 使用threadpool import time import threadpool import threading def process(data): """消费者""" print("%s process to %s" % (threading.current_thread().getName(), data)) time.sleep(1) return data def callback(request, result): """回调函数,用于取回结果""" print(f"callback result = {result}") def main(): start_time = time.time() # 创建线程池 pool = threadpool.ThreadPool(3) # 创建任务 requests_ = threadpool.makeRequests(process, range(10), callback) # 创建任务 for req in requests_: pool.putRequest(req) pool.wait() print(f"{threading.current_thread().getName()} 用时:{time.time() - start_time}") if __name__ == '__main__': main() # 使用ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor def process(data): """消费者""" print("%s process to %s" % (threading.current_thread().getName(), data)) time.sleep(1) return data def main(): start_time = time.time() # 创建线程池 with ThreadPoolExecutor(3) as executor: # 提交任务给线程池 results = [executor.submit(process, i) for i in range(10)] # 获取任务的结果 for future in results: print(f"callback result = {future.result()}") print(f"{threading.current_thread().getName()} 用时:{time.time() - start_time}") if __name__ == '__main__': main() ``` 这些示例代码演示了如何在Python函数中使用线程池,您可以根据自己的需求选择适合的方式进行使用。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [python 线程池使用](https://blog.csdn.net/weixin_45459224/article/details/126182031)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *3* [Python函数综合案例.md](https://download.csdn.net/download/qq_42431718/88241199)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值