Python最广为使用的并发库futures使用入门与内部原理

Python学习:Python并发编程之Futures 我们每天在各个网站、各个 App 上获取的新闻信息,很大一部分便是通过并发编程版的爬虫获得。正确合理地使用并发编程,无疑会给我们的程序带来极大的性能提升。今天学习理解、运用 Python 中的并发编程——Futures。 一、区分并发和并行 在操作系统中,并发和并行的概念如下: 并发:在操作系统中,是指一个时间段中有几个程序都处于已启动运行到运行完毕之间,且这几个程序都是在同一个处理机上运行,但任一个时刻点上只有一个程序在处理机上运行。简言之,是指系统具有处理多个任务的能力。 并行:当系统有一个以上 阅读详情

在使用Python处理任务时,限于单线程处理能力有限,需要将任务并行化,分散到多个线程或者是多个进程去执行。

concurrent.futures就是这样一种库,它可以让用户可以非常方便的将任务并行化。这个名字有点长,后面我直接使用词汇concurrent来代替concurrent.futures。

640?wx_fmt=jpeg

concurrent提供了两种并发模型,一个是多线程ThreadPoolExecutor,一个是多进程ProcessPoolExecutor。对于IO密集型任务宜使用多线程模型。对于计算密集型任务应该使用多进程模型。

为什么要这样选择呢?是因为Python GIL的存在让Python虚拟机在进行运算时无法有效利用多核心。对于纯计算任务,它永远最多只能榨干单个CPU核心。如果要突破这个瓶颈,就必须fork出多个子进程来分担计算任务。而对于IO密集型任务,CPU使用率往往是极低的,使用多线程虽然会加倍CPU使用率,但是还远远到不了饱和(100%)的地步,在单核心可以应付整体计算的前提下,自然是应该选择资源占用少的模式,也就是多线程模式。

接下来我们分别尝试一下两种模式来进行并行计算。

多线程

640?wx_fmt=png

多线程模式适合IO密集型运算,这里我要使用sleep来模拟一下慢速的IO任务。同时为了方便编写命令行程序,这里使用Google fire开源库来简化命令行参数处理。

# coding: utf8
# t.py
import time
import fire
import threading
from concurrent.futures import ThreadPoolExecutor, wait

# 分割子任务
def each_task(index):
    time.sleep(1)  # 睡1s,模拟IO
    print "thread %s square %d" % (threading.current_thread().ident, index)
    return index * index  # 返回结果


def run(thread_num, task_num):
    # 实例化线程池,thread_num个线程
    executor = ThreadPoolExecutor(thread_num)
    start = time.time()
    fs = []  # future列表
    for i in range(task_num):
        fs.append(executor.submit(each_task, i))  # 提交任务
    wait(fs)  # 等待计算结束
    end = time.time()
    duration = end - start
    s = sum([f.result() for f in fs])  # 求和
    print "total result=%s cost: %.2fs" % (s, duration)
    executor.shutdown()  # 销毁线程池


if __name__ == '__main__':
    fire.Fire(run)

运行python t.py 2 10,也就是2个线程跑10个任务,观察输出

thread 123145422131200 square 0thread 123145426337792 square 1
thread 123145426337792 square 2
 thread 123145422131200 square 3
thread 123145426337792 square 4
thread 123145422131200 square 5
thread 123145426337792 square 6
thread 123145422131200 square 7
thread 123145426337792 square 8
thread 123145422131200 square 9
total result=285 cost: 5.02s

我们看到计算总共花费了大概5s,总共sleep了10s由两个线程分担,所以是5s。读者也许会问,为什么输出乱了,这是因为print操作不是原子的,它是两个连续的write操作合成的,第一个write输出内容,第二个write输出换行符,write操作本身是原子的,但是在多线程环境下,这两个write操作会交错执行,所以输出就不整齐了。如果将代码稍作修改,将print改成单个write操作,输出就整齐了(关于write是否绝对原子性还需要进一步深入讨论)

# 分割子任务
def each_task(index):
    time.sleep(1)  # 睡1s,模拟IO
    import sys
    sys.stdout.write("thread %s square %d\n" % (threading.current_thread().ident, index))
    return index * index  # 返回结果

我们再跑一下python t.py 2 10,观察输出

thread 123145438244864 square 0
thread 123145442451456 square 1
thread 123145442451456 square 2
thread 123145438244864 square 3
thread 123145438244864 square 4
thread 123145442451456 square 5
thread 123145438244864 square 6
thread 123145442451456 square 7
thread 123145442451456 square 9
thread 123145438244864 square 8
total result=285 cost: 5.02s

接下来,我们改变参数,扩大到10个线程,看看所有任务总共需要多久完成

> python t.py 10 10
thread 123145327464448 square 0
thread 123145335877632 square 2
thread 123145331671040 square 1
thread 123145344290816 square 4
thread 123145340084224 square 3
thread 123145348497408 square 5
thread 123145352704000 square 6
thread 123145356910592 square 7
thread 123145365323776 square 9
thread 123145361117184 square 8
total result=285 cost: 1.01s

可以看到1s中就完成了所有的任务。这就是多线程的魅力,可以将多个IO操作并行化,减少整体处理时间。

多进程

640?wx_fmt=png

相比多线程适合处理IO密集型任务,多进程适合计算密集型。接下来我们要模拟一下计算密集型任务。我的个人电脑有2个核心,正好可以体验多核心计算的优势。

那这个密集型计算任务怎么模拟呢,我们可以使用圆周率计算公式。

640?wx_fmt=png

通过扩大级数的长度n,就可以无限逼近圆周率。当n特别大时,计算会比较缓慢,这时候CPU就会一直处于繁忙状态,这正是我们所期望的。

好,下面开写多进程并行计算代码

# coding: utf8
# p.py
import os
import sys
import math
import time
import fire
from concurrent.futures import ProcessPoolExecutor, wait

# 分割子任务
def each_task(n):
    # 按公式计算圆周率
    s = 0.0
    for i in range(n):
        s += 1.0/(i+1)/(i+1)
    pi = math.sqrt(6*s)
    # os.getpid可以获得子进程号
    sys.stdout.write("process %s n=%d pi=%s\n" % (os.getpid(), n, pi))
    return pi
    
def run(process_num, *ns):  # 输入多个n值,分成多个子任务来计算结果
    # 实例化进程池,process_num个进程
    executor = ProcessPoolExecutor(process_num)
    start = time.time()
    fs = []  # future列表
    for n in ns:
        fs.append(executor.submit(each_task, int(n)))  # 提交任务
    wait(fs)  # 等待计算结束
    end = time.time()
    duration = end - start
    print "total cost: %.2fs" % duration
    executor.shutdown()  # 销毁进程池


if __name__ == '__main__':
    fire.Fire(run)

通过代码可以看出多进程模式在代码的编写上和多线程没有多大差异,仅仅是换了一个类名,其它都一摸一样。这也是concurrent库的魅力所在,将多线程和多进程模型抽象出了一样的使用接口。

接下来我们运行一下python p.py 1 5000000 5001000 5002000 5003000,总共计算4次pi,只用一个进程。观察输出

process 96354 n=5000000 pi=3.1415924626
process 96354 n=5001000 pi=3.14159246264
process 96354 n=5002000 pi=3.14159246268
process 96354 n=5003000 pi=3.14159246272
total cost: 9.45s

可以看出来随着n的增大,结果越来越逼近圆周率,因为只用了一个进程,所以任务是串行执行,总共花了大约9.5s。

接下来再增加一个进程,观察输出

> python p.py 2 5000000 5001000 5002000 5003000
process 96529 n=5001000 pi=3.14159246264
process 96530 n=5000000 pi=3.1415924626
process 96529 n=5002000 pi=3.14159246268
process 96530 n=5003000 pi=3.14159246272
total cost: 4.98s

从耗时上看缩短了接近1半,说明多进程确实起到了计算并行化的效果。此刻如果使用top命令观察进程的CPU使用率,这两个进程的CPU使用率都占到了接近100%。

如果我们再增加2个进程,是不是还能继续压缩计算时间呢

> python p.py 4 5000000 5001000 5002000 5003000
process 96864 n=5002000 pi=3.14159246268
process 96862 n=5000000 pi=3.1415924626
process 96863 n=5001000 pi=3.14159246264
process 96865 n=5003000 pi=3.14159246272
total cost: 4.86s

看来耗时不能继续节约了,因为只有2个计算核心,2个进程已经足以榨干它们了,即使再多加进程也只有2个计算核心可用。

深入原理

concurrent用的时候非常简单,但是内部实现并不是很好理解。在深入分析内部的结构之前,我们需要先理解一下Future这个对象。在前面的例子中,executor提交(submit)任务后都会返回一个Future对象,它表示一个结果的坑,在任务刚刚提交时,这个坑是空的,一旦子线程运行任务结束,就会将运行的结果塞到这个坑里,主线程就可以通过Future对象获得这个结果。简单一点说,Future对象是主线程和子线程通信的媒介。

640?wx_fmt=png

Future对象的内部逻辑简单一点可以使用下面的代码进行表示

class Future(object):

    def __init__(self):
        self._condition = threading.Condition()  # 条件变量
        self._result = None

    def result(self, timeout=None):
        self._condition.wait(timeout)
        return self._result

    def set_result(self, result):
        self._result = result
        self._condition.notify_all()

主线程将任务塞进线程池后得到了这个Future对象,它内部的_result还是空的。如果主线程调用result()方法获取结果,就会阻塞在条件变量上。如果子线程计算任务完成了就会立即调用set_result()方法将结果填充进future对象,并唤醒阻塞在条件变量上的线程,也就是主线程。这时主线程立即醒过来并正常返回结果。

线程池内部结构

主线程和子线程交互分为两部分,第一部分是主线程如何将任务传递给子线程,第二部分是子线程如何将结果传递给主线程。第二部分已经讲过了是通过Future对象来完成的。那第一部分是怎么做到的呢?

640?wx_fmt=png

如上图所示,秘密就在于这个队列,主线程是通过队列将任务传递给多个子线程的。一旦主线程将任务塞进任务队列,子线程们就会开始争抢,最终只有一个线程能抢到这个任务,并立即进行执行,执行完后将结果放进Future对象就完成了这个任务的完整执行过程。

线程池的缺点

concurrent的线程池有个重大的设计问题,那就是任务队列是无界的。如果队列的生产者任务生产的太快,而线程池消费太慢处理不过来,任务就会堆积。如果堆积一直持续下去,内存就会持续增长直到OOM,任务队列里堆积的所有任务全部彻底丢失。用户使用时一定要注意这点,并做好适当的控制。

进程池内部结构

进程池内部结构复杂,连concurent库的作者自己也觉得特别复杂,所以在代码里专门画了一张ascii图来讲解模型内部结构

640?wx_fmt=png
640?wx_fmt=png

我觉得作者的这张图还不够好懂,所以也单独画了一张图,请读者们仔细结合上面两张图,一起来过一边完整的任务处理过程。

  1. 主线程将任务塞进TaskQueue(普通内存队列),拿到Future对象

  2. 唯一的管理线程从TaskQueue获取任务,塞进CallQueue(分布式跨进程队列)

  3. 子进程从CallQueue中争抢任务进行处理

  4. 子进程将处理结果塞进ResultQueue(分布式跨进程队列)

  5. 管理线程从ResultQueue中获取结果,塞进Future对象

  6. 主线程从Future对象中拿到结果

这个复杂的流程中涉及到3个队列,还有中间附加的管理线程。那为什么作者要设计的这么复杂,这样的设计有什么好处?

首先,我们看这张图的左半边,它和线程池的处理流程没有太多区别,区别仅仅是管理线程只有一个,而线程池的子线程会有多个。这样设计可以使得多进程模型和多线程模型的使用方法保持一致,这就是为什么两个模型使用起来没有任何区别的原因所在——通过中间的管理线程隐藏了背后的多进程交互逻辑。

然后我们再看这张图的右半边,管理线程通过两个队列来和子进程们进行交互,这两个队列都是跨进程队列(multiprocessing.Queue)。CallQueue是单生产者多消费者,ResultQueue是多生产者单消费者。

CallQueue是个有界队列,它的上限在代码里写死了为「子进程数+1」。如果子进程们处理不过来,CallQueue就会变满,管理线程就会停止往里面塞数据。但是这里也遭遇了和线程池一样的问题,TaskQueue是无界队列,它的内容可不管消费者是否在持续(管理线程)消费,TaskQueue会无限制的持续生长,于是最终也会会导致OOM。

跨进程队列

进程池模型中的跨进程队列是用multiprocessing.Queue实现的。那这个跨进程队列内部细节是怎样的,它又是用什么高科技来实现的呢

笔者仔细阅读了multiprocessing.Queue的源码发现,它使用无名套接字sockerpair来完成的跨进程通信,socketpair和socket的区别就在于socketpair不需要端口,不需要走网络协议栈,通过内核的套接字读写缓冲区直接进行跨进程通信。

640?wx_fmt=png

当父进程要传递任务给子进程时,先使用pickle将任务对象进行序列化成字节数组,然后将字节数组通过socketpair的写描述符写入内核的buffer中。子进程接下来就可以从buffer中读取到字节数组,然后再使用pickle对字节数组进行反序列化来得到任务对象,这样总算可以执行任务了。同样子进程将结果传递给父进程走的也是一样的流程,只不过这里的socketpair是ResultQueue内部创建的无名套接字。

multiprocessing.Queue是支持双工通信,数据流向可以是父到子,也可以是子到父,只不过在concurrent的进程池实现中只用到了单工通信。CallQueue是从父到子,ResultQueue是从子到父。

总结

concurrent.futures框架非常好用,虽然内部实现机制异常复杂,读者也无需完全理解内部细节就可以直接使用了。但是需要特别注意的是不管是线程池还是进程池其内部的任务队列都是无界的,一定要避免消费者处理不及时内存持续攀升的情况发生。

近日, 老钱的新书《深入理解RPC》正式上线,限时优惠9.9元,感兴趣的读者扫描下面的二维码进入小册首页来阅读。还可以点击阅读原文进入小册首页。老钱的文章内容一如既往——带读者透过技术表面形式看穿深度内核本质。

640?wx_fmt=other

640?wx_fmt=jpeg

名人推荐

640?wx_fmt=other

640?wx_fmt=other

读者福利

作者决定拿出一部分自己收藏的好书作为福利,以抽奖的形式免费赠予购买小册的用户(国内用户包邮),数量有限,大家就拼人品吧(抽奖规则时间在读者微信群同步)。

  1. 《C Primer Plus》

  2. 《动手玩转 Arduino》

  3. 《白帽子讲 Web 安全》

  4. 《深入理解程序设计——使用 Linux 汇编语言》

  5. 《图解服务器网络架构》

  6. 《构建 Oracle 高可用环境》

等等,除了以上 6 本外,还有很多其它的好书,这里就不一一列举了。

640?wx_fmt=jpeg

阅读更多高级文章,关注公众号【码洞】

点击阅读原文,进入小册首页

Python Futures 对象 阅读详情

相关推荐

Python 中 concurrent.futures 模块使用说明

Python 中 concurrent.futures 模块使用说明 文章目录Python 中 concurrent.futures 模块使用说明0. 参考资料1. 概述2. Executor Object 执行器对象3. ThreadPoolExecutor 线程池执行器4. ThreadPoolExecutor 例子5. ProcessPoolExecutor 进程池执行器6. Proces...

团子大圆帅的博客 2万+

Python广为使用的并发处理库futures使用入门与内部原理

在使用Python处理任务时,限于单线程处理能力有限,需要将任务并行化,分散到多个线程或者是多个进程去执行。 concurrent.futures就是这样一种库,它可以让用户可以非常方便的将任务并行化。这个名字有点长,后面我直接使用词汇concurrent来代替concurrent.futures。 concurrent提供了两种并发模型,一个是多线程ThreadPoolExecutor,一个是...

weixin_33932129的博客 271

笔记-python concurrent.futures

python因为其全局解释器锁GIL而无法通过线程实现真正的平行计算。这个论断我们不展开,但是有个概念我们要说明,IO密集型 vs. 计算密集型。IO密集型:读取文件,读取网络套接字频繁。计算密集型:大量消耗CPU的数学与逻辑运算,也就是我们这里说的平行计算。而concurrent.futures模块,可以利用multiprocessing实现真正的平行计算。

零正催照 1148

Python 并发编程 Futures

本篇文章介绍了如何使用 Futures 创建线程池和进程池,并提供了两个 case,在实验过程中发现,因为 GIL 的原因 Python 的线程池并不适合 CPU 密集型的应用,此时就需要考虑使用多进程的方式,或者将核心代码使用 C++ 编写通过 Python 调用,从而绕过 GIL。虽然有 GIL 用户依然要考虑线程安全的问题,因为 GIL 只是方便 Python 解释器的编写人员,而不是 Python 程序的编写人员。程序每次执行的结果可能都不相同,结果都不为 0,此时就需要使用线程锁来规避此类问题。

分享既学习 644

Multisim14安装失败?手把手教你彻底清理残留文件(附专用卸载工具)

本文针对Multisim14安装失败问题,提供了一套从根源解决的完整方案。文章详细分析了安装失败的三大元凶——文件残留、注册表残留和隐藏组件,并手把手指导用户使用专用卸载工具进行标准化清理,再结合手动深度清理文件系统和注册表,最终实现彻底卸载和完美重装,有效解决Multisim14安装问题。

weixin_29179311的博客 317

电力电子课设救星:用Simulink实现Buck变换器双闭环控制(PID调参技巧)

本文是电力电子课程设计的实用指南,详细讲解如何利用Simulink仿真平台实现Buck变换器的电压电流双闭环控制。文章核心聚焦于PID参数整定的实战技巧,提供了从手动调试口诀到MATLAB脚本自动优化的完整方案,旨在帮助学生高效完成课设,深入理解闭环控制原理并生成专业报告。

weixin_29179311的博客 296

Python并发编程之Futures

不论是哪一种语言,并发编程都是一项非常重要的技巧。比如我们上一章用的爬虫,就被广泛用在工业的各个领域。我们每天在各个网站、App上获取的新闻信息,很大一部分都是通过并发编程版本的爬虫获得的。 正确并合理的使用并发编程,无疑会给我们的程序带来极大性能上的提升。今天我们就一起学习Python中的并发编程——Futures。 区分并发和并行 我们在学习并发编程时,常常会听到两个词:并发(Concurre...

python学习者的博客 1067

现代SerDes解剖:从CTLE/DFE到PAM4与ADC+DSP架构

高速有线通信的物理层瓶颈,正从并行总线的时钟偏斜转向串行链路的信号完整性挑战。SerDes作为解决这一问题的核心技术,其内部架构远超“并转串”的朴素认知。接收端的CTLE线性均衡与DFE判决反馈均衡,配合CDR时钟恢复,共同构成对抗信道损耗与码间干扰的关键机制。随着速率迈向56G/112G,PAM4调制以电平幅度换取带宽,而ADC+DSP架构则把均衡算法搬入数字域,实现更灵活的非线性补偿。理解这些模块如何协同工作,是调试PCIe、以太网等高速链路、优化眼图余量与抖动预算的基础,也是硬件工程师从盲目调参走向系

weixin_29194817的博客 301

[Python] 使用futures模块处理并发(超好用的并发库)

使用futures模块处理并发 concurrent.futures模块的主要特色是ThreadPoolExecutor和ProcessPoolExecutor类,这两个类实现的接口能分别在不同的线程或进程中执行可调用的对象。这两个类在内部维护着一个工作线程或进程池,以及要执行的任务队列。ThreadPoolExecutor和ProcessPoolExecutor的API接口一样,本文重点讲解ThreadPoolExecutor的用法。 一、用法示例 1. 使用 map() 处理多个任务 使用两行代码,就能

Spade_的博客 3568

人工智能-CS188 Project 2: Multi-agents

一、项目介绍 项目介绍网页 项目代码下载 本项目是采用Berkeley的CS188课程内容实习二的内容,在这个项目中,我们将为经典版本的Pacman 设计自动算法,包括幽灵。在此过程中,我们将实现 minimax 和 expectimax 搜索并尝试评估函数设计 完成作业只需要完成5个题目,按照项目介绍的步骤进行完成,主要是在multiAgents.py文件中进行补充代码 二、代码详情 1、Question1:Reflex Agent 略 2、Question2:Minimax 套用ppt上的minm

xiten的博客 8286

DP83869HM的LWIP性能测试

E:/Xilinx/Vitis/2021.1/data/embeddedsw/ThirdParty/sw_services/lwip211_v2_0/src/contrib/ports/xilinx/netif/xemacpsif_physpeed.c文件中的。然后phy_setup_emacps中调用get_IEEE_phy_speed函数,根据PHY的ID判断出Marvell或TI或Realtek的PHY,调用对应的网速自动协商函数get_TI_phy_speed,该函数中的部分配置需要注释。

FPGA艺术家的博客 357

concurrent.futures.ThreadPoolExecutor并发库详解

concurrent.futures Python3.2带来的新版功能。是Python并发执行的标准库。 这个模块具有线程池和进程池、管理并行编程任务、处理非确定性的执行流程、进程/线程同步等功能。 concurrent.futures 是两个文件放在一起作为这个模块,因为concurrent文件夹下只有futures这个文件夹,而futures下有两个主要文件thread.py和proces...

漫步量化 9210

[Dify] 【实战笔记】优化 Excel 电话簿类知识库检索效果的实战技巧

摘要:文章分析了Dify知识库在处理Excel结构化数据(如电话簿)时出现的检索异常问题,发现主要原因是默认召回多条记录且缺乏精准匹配机制。提出两个核心解决方案:1)优化知识库召回参数,包括调整TopK值、提高关键词权重等;2)设计精准提示词模板,明确约束模型行为范围。通过对比测试验证了优化效果,并给出进阶建议如数据格式转换、输出格式化等。最后针对知识检索类AI应用提供了场景适配建议。

技术小甜甜:传播可落地技术,帮助个人和企业降低解决真实场景问题的成本 369

python concurrent.futures并发库 多进程 多线程

Python3废弃了原来的thread模块,换成了高级的threading模块,concurrent.futures是使用线程的最新方式。(Python3把thread模块重命名为_thread,以此强调这是低层实现, 不应该在应用代码中使用)如果使用场景较复杂,需要更高级的工具multiprocessing模块和threading模块。 concurrent.futures模块提供了一个用于异

很长很长的专栏 8252

Tableau学习教程(万字保姆级教程)​​​​​​

​Tableau学习教程(保姆级教材,学不会私信留言来找我) 从BI系统开发的角度来学习tableau工具,更学会报表开发的流程 内含学习使用数据源,tableau打包工作簿,可直接提取使用 ​...

小菜菜的博客 6万+

一次把 PRIV mode 讲透:从 ABAP 内存分配链路到定位与治理

ABAP系统进入PRIV模式的本质是Extended Memory耗尽时转向Heap内存的应急机制。该现象会导致工作进程被独占,系统吞吐量下降。触发条件包括系统EM资源池耗尽或单个用户超过配额阈值。关键参数em/address_space_MB和ztta/roll_extension需协同调整。典型症状包括SM50显示PRIV状态、响应延迟和内存dump。治理需双管齐下:系统侧合理配置内存参数,代码侧优化内存使用模式,如分页处理、减少大内表操作和下推聚合逻辑。PRIV模式应作为异常信号而非常态目标,健康系统

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 208

python并发库:concurrent.futures的使用

第一次接触futures这个库是在tornado中,4.0的tornado会经常的使用他,因为tornado本身的那个corouting的异步功能,是需要逻辑里面所调用的模块本身就支持异步才可以的。而且如果用futures之后,在一定程度上缓解了这个事情。 concurrent.futures 是python3新增加的一个库,用于并发处理,提供了多线程和多进程的并发功能 类似于其他语言里的线程池(

夜夜夜夜 1万+

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

Python并发——concurrent.futures梳理

Python并发——concurrent.futures梳理 参考官方文档: concurrent.futures — 启动并行任务 Executor对象 class concurrent.funtures.Executor 该抽象类是 ThreadPoolExecutor 和 ProcessPoolExecutor 的父类,提供异步执行调用方法。要通过它的子类调用,而不是直接调用。 submit方法 submit(fn, /, *args, **kwargs) 提交一个可执行对象,fn 将按照如下

weixin_44966641的博客 2357

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

Python 之 concurrent.futures 使用

之前多线程用 threading 的时候比较多,最近看看 concurrent.futures 实现多线程多进程的方法。

Looking的网页备忘录和个人知识库 1019

python——concurrent.futures

用于管理线程池。:用于管理进程池。这两种执行器都实现了同样的接口,因此你可以使用相同的代码逻辑来管理线程和进程。提供了一个方便的接口来管理多线程和多进程的并发执行。通过理解和使用这些 API,你可以更有效地编写并行程序,提高程序的执行效率。在选择使用还是时,应根据任务的性质(I/O 密集型或 CPU 密集型)来决定。

pumpkin84514的博客 2558

SCOOP vs multiprocessing vs concurrent.futures:Python并行方案终极对比

当你的 Python 程序撞上性能瓶颈,并行计算往往是最直接的加速出路。在众多 Python 并行方案中,SCOOP(Scalable COncurrent Operations in Python)与标准库自带的 multiprocessing、concurrent.futures 是最常被摆上台面对比的三驾马车。本文将从运行架构、功能边界、上手成本到真实选型场景,为你做一次完整的 Python

gitblog_00690的博客 758

python异步并发模块concurrent.futures入门详解

目录 1. 模块安装 2. concurrent.futures模块详解 3. 附录:PythonGIL相关 4. 参考文档 concurrent.futures是一个非常简单易用的库,主要用来实现多线程和多进程的异步并发。 本文主要对concurrent.futures库相关模块进行详解,并分别提供了详细的示例demo。 回到目录 1. 模块安装 1) python 3.x中自...

bang152101的博客 1464

requests-futures与asyncio对比:如何选择最适合的异步HTTP方案

在Python异步编程领域,处理HTTP请求是常见需求。requests-futures和asyncio是两种主流的异步HTTP处理方案,它们各有优势和适用场景。本文将深入对比这两种方案,帮助开发者根据项目需求选择最适合的异步HTTP方案。 ## 什么是requests-futures? requests-futures是一个基于requests库的异步扩展,它利用concurrent.fut

gitblog_00181的博客 840

Python的并行编程:multiprocessing与concurrent.futures

1.背景介绍 1. 背景介绍 在现代计算机科学中,并行编程是一种重要的技术,它可以让我们更有效地利用多核处理器和分布式系统来解决复杂的计算问题。Python是一种非常流行的编程语言,它提供了多种并行编程库来帮助开发者实现并行计算。在本文中,我们将深入探讨Python的并行编程,特别关注multiprocessing和concurrent.futures这两个库。 multiprocessin...

AI天才研究院 2560
上一篇: 老钱的处女作《深入理解RPC》正式上线了
下一篇: 国产巨轮——蚂蚁金服SOFA-RPC框架深度分析
码洞
博客等级 码龄10年 41粉丝 137原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值