Python中的并发编程(3)线程池、锁

concurrent.futures 提供的线程池

concurrent.futures模块提供了线程池和进程池简化了多线程/进程操作。

线程池原理是用一个任务队列让多个线程从中获取任务执行,然后返回结果。

常见的用法是创建线程池,提交任务,等待完成并获取结果

with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
	futures = [executor.submit(count, item) for item in number_list] # count是一个函数,item是其参数
	for future in concurrent.futures.as_completed(futures):
		print(future.result())
  • concurrent.futures.ThreadPoolExecutor(max_workers=5)创建了一个线程池,max_workers指定了线程数量上限。通过线程池可以创建和执行任务。
  • concurrent.futures使用Future类表示(未来的)任务。调用.submit()时会创建并执行一个任务(Future)。
  • .as_completed(futures)是一个迭代器,当futures中有任务完成时会产出该future.

Python最广为使用的并发处理库futures使用入门与内部原理 - 知乎 (zhihu.com)对这个过程做了比较好的说明:
在这里插入图片描述
主线程是通过队列将任务传递给多个子线程的。一旦主线程将任务塞进任务队列,子线程们就会开始争抢,最终只有一个线程能抢到这个任务,并立即进行执行,执行完后将结果放进Future对象就完成了这个任务的完整执行过程。

python-parallel-programming-cookbook-cn 1.0 文档 中的一个例子对使用顺序执行、线程池进程池三种方式进行计算的时间进行了比较:

import concurrent.futures
import time


# 一个耗时的计算
def count(number) :
    for i in range(0, 10000000):
        i=i+1
    return i * number

if __name__ == "__main__":
    number_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
    # 顺序执行
    start_time = time.time()
    for item in number_list:
        print(count(item))
    print("Sequential execution in " + str(time.time() - start_time), "seconds")
    # 线程池
    start_time_1 = time.time()
    with concurrent.futures.ThreadPoolExecutor(max_workers=5) as executor:
        futures = [executor.submit(count, item) for item in number_list]
        for future in concurrent.futures.as_completed(futures):
            print(future.result())
    print("Thread pool execution in " + str(time.time() - start_time_1), "seconds")
    
    # 进程池
    start_time_2 = time.time()
    with concurrent.futures.ProcessPoolExecutor(max_workers=5) as executor:
        futures = [executor.submit(count, item) for item in number_list]
        for future in concurrent.futures.as_completed(futures):
            print(future.result())
    print("Process pool execution in " + str(time.time() - start_time_2), "seconds")

结果为:

Sequential execution in 7.095552206039429 seconds
Thread pool execution in 7.140377998352051 seconds
Process pool execution in 4.240718126296997 seconds

竞争和锁

由于共享内存,多线程程序容易遇到竞争问题:两个内存对同一个变量进行修改可能导致意想不到的问题。

看下面这个计数的例子:
我们创建了一个全局变量thread_visits,在visit_counter()中修改这个变量值。

from threading import Thread
thread_visits = 0
def visit_counter():
    global thread_visits
    for _ in range(100_000):  
        thread_visits +=  1 #  thread_visits = thread_visits + 1

if __name__ == "__main__":
    thread_count = 100
    threads = [
        Thread(target=visit_counter)
        for _ in range(thread_count)
    ]
    for thread in threads:
        thread.start()
    for thread in threads:
        thread.join()
    print(f"thread_count={thread_count}, thread_visits={thread_visits}")

执行结果:

第1次 :thread_count=100, thread_visits=7227793
第2次 :thread_count=100, thread_visits=9544020
第3次 :thread_count=100, thread_visits=9851811

执行该程序会发现每次运行thread_visits的值都不一样。
因为在 thread_visits 变量上的读取和写入操作之间有一段时间,另一个线程可以介入并操作结果。这导致了竞争。
在这里插入图片描述

(线程1和线程2对变量thread_visits的竞争。两个线程都对thread_visits执行了+1的操作,但最后thread_visits的是1,而不是2。)

thread_visits += 1 实际包含读写两个操作,它等价于
thread_visits = thread_visits + 1,先读取thread_visits的值并+1,再写入到thread_visits

正确方法是使用保证一次只有一个线程可以处理单个代码块
在这里插入图片描述

from threading import Thread
from threading import Lock

thread_visits = 0
thread_visits_lock = Lock()

def visit_counter():
    global thread_visits
    for _ in range(100_000):  
        with thread_visits_lock:
            thread_visits +=  1 #  thread_visits = thread_visits + 1

运行结果:

thread_count=100, thread_visits=10000000

这次我们得到了正确的结果,但花费了接近一分钟的时间。因为受保护的块不能并行运行。此外,获取和释放锁是需要一些额外操作。

将锁放在外面的时候,会发现花费的时间减少了很多。因为减少了获取和释放锁的消耗。

	with thread_visits_lock:
        for _ in range(100_000):  
            thread_visits +=  1 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/236446.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Nginx正则表达式

目录 1.nginx常用的正则表达式 2.location location 大致可以分为三类 location 常用的匹配规则 location 优先级 location 示例说明 优先级总结 3.rewrite rewrite功能 rewrite跳转实现 rewrite执行顺序 语法格式 rewrite示例 实例1: 实例2&#xf…

2023年阿里云云栖大会-核心PPT资料下载

一、峰会简介 历经14届的云栖大会,是云计算产业的建设者、推动者、见证者。2023云栖大会以“科技、国际、年轻”为基调,以“计算,为了无法计算的价值”为主题,发挥科技平台汇聚作用,与云计算全产业链上下游的先锋代表…

网线市场现状与发展趋势预测

随着物联网、5G、云计算等技术的迅速发展,全球对于高速、稳定的网络需求急剧增长,这进一步推动了网线市场的发展。各种网络应用场景,从家庭到企业、数据中心到智能城市,都需要大量的高质量网线来支持数据传输和通信需求。本文将对…

LinuxBasicsForHackers笔记 -- 管理用户环境变量

查看和修改环境变量 env – 您可以通过从任何目录在终端中输入 env 来查看所有默认环境变量。环境变量的名称始终为大写,如 HOME、PATH、SHELL 等。 查看所有环境变量 set – 查看所有环境变量,包括 shell 变量、局部变量和 shell 函数(例…

Axure的安装及基本功能介绍

目录 一. Axure概述 二. Axure安装 2.1 安装包下载 2.2 安装步骤 三. Axure功能介绍​ 3.1 工具栏介绍 3.1.1 复制,剪切及粘贴 3.1.2 选择模式和连接 3.1.3 插入形状 3.1.4 点(编辑控点) 3.1.5 置顶和置底 3.1.6 组合和取消组合 …

利用Rclone将阿里云对象存储迁移至雨云对象存储的教程,对象存储数据迁移教程

使用Rclone将阿里云对象存储(OSS)的文件全部迁移至雨云对象存储(ROS)的教程,其他的对象存储也可以参照本教程。 Rclone简介 Rclone 是一个用于和同步云平台同步文件和目录命令行工具。采用 Go 语言开发。 它允许在文件系统和云存储服务之间或在多个云存储服务之间…

RE2文本匹配调优实战

引言 在RE2文本匹配实战的最后,博主说过会结合词向量以及其他技巧来对效果进行调优,本篇文章对整个过程进行详细记录。其他文本匹配系列实战后续也会进行类似的调优,方法是一样的,不再赘述。 本文所用到的词向量可以在Gensim训练…

如何用CHAT写方案?

问CHAT:帮我写一份航空无动力乐园的可执行方案 CHAT回复: 方案一:概念及地点筛选 航空无动力乐园是指以航空运动为主题,利用自然地形与风力进行滑翔、跳伞等无动力航空运动的户外休闲娱乐乐园。鉴于此,首需要确定乐园…

Java入门项目--蚂蚁爱购

简介 这是一个靠谱的Java入门项目实战,名字叫蚂蚁爱购。 从零开发项目,视频加文档,十天就能学会开发JavaWeb项目,教程路线是:搭建环境> 安装软件> 创建项目> 添加依赖和配置> 通过表生成代码> 编写Ja…

力扣111. 二叉树的最小深度

给定一个二叉树,找出其最小深度。 最小深度是从根节点到最近叶子节点的最短路径上的节点数量。 说明:叶子节点是指没有子节点的节点。 示例 1: 输入:root [3,9,20,null,null,15,7] 输出:2 示例 2: 输入…

这样的Python自动化测试面试题,测开来了都不一定都会把!

十、接口自动化 10.1 接口自动化怎么测试 ( Python requestspytest 版本) 原来我们接口自动化是用 python request pytest 执行 接口自动化其实主要就是接口测试的基础上填加了断言,参数化,动态关联 做接口自动化之前,我们也会划分模块&#…

【数据结构】C语言实现堆(附完整运行代码)

🦄个人主页:修修修也 🎏所属专栏:数据结构 ⚙️操作环境:Visual Studio 2022 目录 一.了解项目功能 二.项目功能演示(以大堆为例) 三.逐步实现项目功能模块及其逻辑详解 1.实现堆程序主函数 2.创建堆结构 3.堆的初始化 4.数据元素入堆 5.数据元素…

Linux上编译和测试V8引擎源码

介绍 V8引擎是一款高性能的JavaScript引擎,广泛应用于Chrome浏览器和Node.js等项目中。在本篇博客中,我们将介绍如何在Linux系统上使用depot_tools工具编译和测试V8引擎源码。 步骤一:安装depot_tools depot_tools是一个用于Chromium开发…

边缘智能网关如何应对环境污染难题

随着我国工业化、城镇化的深入推进,包括大气污染在内的环境污染防治压力继续加大。为应对环境污染防治难题,佰马综合边缘计算、物联网、智能感知等技术,基于边缘智能网关打造环境污染实时监测、预警及智能干预方案,可应用于大气保…

【华为OD题库-076】执行时长/GPU算力-Java

题目 为了充分发挥GPU算力,需要尽可能多的将任务交给GPU执行,现在有一个任务数组,数组元素表示在这1秒内新增的任务个数且每秒都有新增任务。 假设GPU最多一次执行n个任务,一次执行耗时1秒,在保证GPU不空闲情况下&…

ELK综合案例

综合案例 ELKfilebeatnginxjson nginx配置 1,在nginx服务器上安装nginx # yum install epel-release # yum install nginx 2,将nginx日志改成json格式,这样各个字段就方便最终在kibana进行画图统计了 # vim /etc/nginx/nginx.conf ​ http {log_format main $remote_ad…

解决Git提交错误分支

如果 Git 提交到错误的分支&#xff0c;可以通过以下步骤将其转移到正确的分支上&#xff1a; 1.检查当前所在的分支&#xff0c;可以通过 git branch 命令查看。 git branch2.切换到正确的分支&#xff0c;可以通过 git checkout <正确的分支名> 命令进行切换。 git …

windows系统proteus中Ardunio Mega 2560和虚拟机上Ubuntu系统CuteCom进行串口通信

在文章利用proteus实现串口助手和arduino Mega 2560的串口通信-CSDN博客 中&#xff0c;实现了windows系统的proteus中Ardunio Mega 2560和SSCOM通过虚拟串口进行通信。虚拟串口的连接示意图如下图所示。 在文章windows系统和虚拟机上ubuntu系统通过虚拟串口进行通信-CSDN博客…

高级网工在Linux服务器抓包,少不了这几条常用的tcpdump命令。

Linux 的命令太多&#xff0c;tcpdump 是一个非常强大的抓包命令。有时候想看线上发生的一些问题&#xff1a; nginx 有没有客户端连接过来…… 客户端连接过来的时候 Post 上来的数据对不对…… 我的 Redis 实例到底是哪些业务在使用…… tcpdump 作为网络分析神器就派上用场…

2023年四川网信人才技能大赛 实操赛Writeup

文章目录 Crypto比base64少的baseaffine简单的RSA Misc不要动我的flagSimpleUSB猜猜我是谁不聪明的AI Pwngetitezbbstack Reverse谁的DNA动了Dont Touch Me Weblittle_gamejustppbezbbssmart 题目附件&#xff0c;文章末尾微信公众号点点关注亲&#xff0c;谢谢亲~ 题目附件链接…