Python多线程和线程池的下载实战用法

1.多线程和线程池用法区别

多线程和线程池都是Python中常用的并发编程方式,根据具体的需求和场景选择合适的方式。

  1. 多线程:

    • 优点:直观、简单,适合简单的并发任务。可以使用 threading.Thread 类创建线程,每个线程独立执行任务。
    • 缺点:线程的创建和销毁需要时间和资源消耗,如果任务数量较多,频繁地创建和销毁线程可能会影响性能。
  2. 线程池:

    • 优点:线程池可以重复利用已创建的线程,减少了线程创建和销毁的开销,提高了效率。可以使用 concurrent.futures.ThreadPoolExecutor 类创建线程池,并通过提交任务给线程池来执行任务。
    • 缺点:线程池的大小有限,如果任务数量超过线程池的最大工作线程数,任务就会排队等待执行。

根据实际情况,我们可以根据以下几个因素来选择合适的方式:

  • 任务类型:如果是一些独立无关、简单的任务,使用多线程即可;如果是需要复用线程、有限制的任务,使用线程池更合适。
  • 资源消耗:如果任务数量非常庞大,频繁地创建和销毁线程会浪费大量的资源,此时线程池更适合。
  • 控制并发度:线程池可以限制并发的最大数量,避免资源过度占用,控制并发度。

总结来说,多线程和线程池都有各自的优缺点,具体选择哪种方式取决于任务的特性和需求。如果任务数量较少且简单,多线程足够;如果任务数量较大且需要复用线程,线程池更合适。

2.Python线程池的实战下载用法

写法一:

当使用线程池进行下载时,如果需要为每个请求设置延迟而又不想使用time.sleep(),可以通过requests.get()函数的timeout参数来实现。这个参数不仅可以用于设置超时时间,还可以用于模拟延迟。

下面是一个示例代码,演示了如何使用Python线程池进行下载,并在每个请求中加入延迟:

import requests
import concurrent.futures
import timeurls = ['https://www.example.com/file1.txt','https://www.example.com/file2.txt','https://www.example.com/file3.txt','https://www.example.com/file4.txt','https://www.example.com/file5.txt']def download_with_delay(url):print(f'Downloading {url}')response = requests.get(url, timeout=15)  # 设置连接和读取超时时间为5秒if response.status_code == 200:with open(url.split('/')[-1], 'wb') as f:f.write(response.content)print(f'{url} has been downloaded successfully')else:print(f'Failed to download {url}')with concurrent.futures.ThreadPoolExecutor() as executor:results = [executor.submit(download_with_delay, url) for url in urls]

在上述示例中,我们定义了一个download_with_delay()函数来进行下载,并使用requests.get()函数的timeout参数来设置连接和读取的超时时间为5秒。这样做不仅可以控制超时,还可以模拟延迟的效果。

需要注意的是,timeout参数的第一个值是连接超时时间,第二个值是读取超时时间。在这个例子中,我们将它们都设置为5秒,你可以根据实际情况进行调整。

写法二

在 Python 中使用线程池进行下载可以通过 concurrent.futures 模块来实现,这里可以使用 ThreadPoolExecutor 来创建线程池。同时,你可以通过 requests.get() 方法的 timeout 参数来模拟延迟,而不使用 time.sleep()

在确定线程池中的 max_workers 参数时,通常可以根据系统的 CPU 核心数量来进行设置。一般情况下,将 max_workers 设置为 CPU 核心数量的 2 到 4 倍是比较合理的选择,具体取决于网络带宽和目标服务器的性能。

下面是一个示例代码,演示了如何使用线程池进行下载,并设置请求超时以模拟延迟:

import requests
import concurrent.futuresurls = ['https://www.example.com/file1.txt','https://www.example.com/file2.txt','https://www.example.com/file3.txt','https://www.example.com/file4.txt','https://www.example.com/file5.txt']def download_file(url):print(f'Downloading {url}')try:response = requests.get(url, timeout=(5, 5))  # 设置连接和读取超时时间为5秒if response.status_code == 200:with open(url.split('/')[-1], 'wb') as f:f.write(response.content)print(f'{url} has been downloaded successfully')else:print(f'Failed to download {url}')except requests.exceptions.RequestException as e:print(f'Error occurred while downloading {url}: {e}')with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:  # 根据实际情况调整 max_workers 的数量executor.map(download_file, urls)

在上述示例中,我们使用了 concurrent.futures.ThreadPoolExecutor 创建了一个拥有多个线程的线程池,并使用 executor.map() 方法来提交下载任务。同时,我们在 requests.get() 方法中设置了超时参数来模拟延迟。

需要注意的是,max_workers 参数的值应该根据实际情况进行调整。一般来说,可以先尝试将其设置为 8 或 16,然后根据下载速度和系统负载情况进行调整。

线程池两种写法的区别

这两段代码都使用了 concurrent.futures.ThreadPoolExecutor 来进行并发任务的执行,但有一些细微的区别。

第一段代码使用了列表推导式和 executor.submit() 方法来提交任务到线程池。它会遍历 urls 列表,对于每个 URL 调用 download_with_delay() 方法,并通过 executor.submit() 将任务提交给线程池。这样可以异步地执行每个任务,并返回一个 Future 对象,可以用于获取任务的结果或异常。

with concurrent.futures.ThreadPoolExecutor() as executor:[executor.submit(download_with_delay, url) for url in urls]

第二段代码使用了 executor.map() 方法来提交任务到线程池。与第一段代码不同,它不需要使用列表推导式,而是直接将 urls 列表作为参数传递给 executor.map() 方法。executor.map() 方法会自动遍历 urls 列表,并以函数 download_file() 为参数,将每个 URL 作为输入进行调用。这样可以更简洁地实现并发执行任务的操作。

with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:executor.map(download_file, urls)

总结起来,两者的主要区别在于任务提交的方式。第一段代码使用 executor.submit() 提交单个任务,适用于需要对每个任务进行额外处理的情况。而第二段代码使用 executor.map() 提交批量任务,适用于简单地对每个任务进行相同处理的情况。

executor.map() 的拓展

executor.map() 中,参数 urls 需要是一个可迭代的对象,例如列表、元组或集合。字典类型是不支持的,因为它不是可迭代的对象。

如果你想要使用字典类型,你可以将字典的键或值转换为一个可迭代的对象,然后将该对象传递给 executor.map()。例如,你可以使用 urls.keys()urls.values() 来获取字典的键或值的可迭代对象,然后将其传递给 executor.map()

下面是一个示例代码,演示了如何将字典的键作为可迭代对象传递给 executor.map()

import concurrent.futuresdef download_file(url):# 下载文件的逻辑passurls = {'file1': 'http://example.com/file1','file2': 'http://example.com/file2','file3': 'http://example.com/file3'
}with concurrent.futures.ThreadPoolExecutor(max_workers=8) as executor:executor.map(download_file, urls.keys())

在上述代码中,urls.keys() 返回字典 urls 的键的可迭代对象,然后将其作为参数传递给 executor.map() 方法。

同样的方式也适用于将字典的值作为可迭代对象传递给 executor.map()

python多线程的实战下载用法

使用Python多线程进行下载可以提高下载速度,并且可以同时下载多个文件,从而提高下载效率。下面是一个示例代码,演示了如何使用Python多线程进行下载:

import requests
import threadingurls = ['https://www.example.com/file1.txt','https://www.example.com/file2.txt','https://www.example.com/file3.txt','https://www.example.com/file4.txt','https://www.example.com/file5.txt']def download_file(url):print(f'Downloading {url}')response = requests.get(url)if response.status_code == 200:with open(url.split('/')[-1], 'wb') as f:f.write(response.content)print(f'{url} has been downloaded successfully')else:print(f'Failed to download {url}')threads = []
for url in urls:thread = threading.Thread(target=download_file, args=(url,))threads.append(thread)thread.start()for thread in threads:thread.join()

在上述示例中,我们定义了一个download_file()函数来进行下载,然后使用threading.Thread类来创建线程,并将每个文件的下载任务分配给不同的线程。最后,我们等待所有线程都结束执行,才退出程序。

需要注意的是,使用多线程下载时,要注意控制并发数量,避免对服务器造成过大的压力。此外,在下载大文件时,还需要考虑文件的分块下载和断点续传等问题,以避免网络故障或其他异常情况导致下载失败。

拓展1:线程池最大线程数的设定

当确定线程池的大小时,通常建议将线程池的最大线程数设置为CPU核心数的2到4倍。根据你提供的信息,如果你的计算机具有16个CPU核心,那么一个合理的线程池大小范围可能是32到64。

然而,线程池的最佳大小也取决于其他因素,例如你的应用程序的性质、任务的类型和计算机的其他负载等。在设置线程池大小时,你可以进行实验并观察应用程序的性能表现,以找到最佳的线程池大小。

另外,还要注意,线程池的大小不一定是越大越好。如果线程池过大,可能会导致资源竞争和上下文切换开销增加,从而降低性能。因此,需要根据具体情况找到适合你应用程序的最佳线程池大小。

拓展2:进程池的用法

进程池是Python中常用的并发编程方式之一,主要用于处理I/O密集型任务,如文件读写、网络请求等。相比于多线程,进程池具有以下几个优点:

  1. 进程之间相互独立,不会出现一个进程挂掉导致其他进程也受到影响的情况。
  2. 进程池能够更好地利用多核CPU,并且在进程切换时,不会受到GIL的限制。
  3. 进程池可以通过限制最大工作进程数来控制并发度,避免资源过度占用。

因此,当我们需要处理大量的I/O密集型任务时,可以选择使用进程池。例如,在爬取网页数据时,每个线程需要等待网络IO操作完成才能继续执行,此时使用进程池能够更好地利用CPU资源,提高程序效率。

下面是一个简单的使用multiprocessing.Pool创建进程池的示例:

import multiprocessingdef task(task_name):print(f"Task {task_name} is running in process {multiprocessing.current_process().name}")# do something...if __name__ == '__main__':# 创建进程池with multiprocessing.Pool(processes=4) as pool:# 提交任务给进程池for i in range(10):pool.apply_async(task, args=(f"Task-{i}",))# 等待所有任务执行完成pool.close()pool.join()

在上面的示例中,我们首先创建了一个包含4个工作进程的进程池。然后,使用 apply_async() 方法向进程池提交了10个任务,每个任务都会打印相应的消息,并执行一些操作。

最后,我们使用进程池的 close()join() 方法等待所有任务完成并释放资源。

你可以根据实际需求,将具体的任务函数替换到 task 函数中,并调整进程池的大小和具体的任务提交方式。

拓展3:通过Python查看CPU信息

你可以使用 Python 中的 os 模块来查看 CPU 的信息。具体来说,可以使用以下代码来获取 CPU 的逻辑个数、物理核心个数和当前使用率:

import os# 获取CPU逻辑个数
cpu_count = os.cpu_count()
print("CPU 逻辑个数:", cpu_count)# 获取CPU物理核心个数
with open("/proc/cpuinfo", "r") as f:cpuinfo = f.readlines()processor_count = 0
for line in cpuinfo:if "physical id" in line:processor_count += 1print("CPU 物理核心个数:", processor_count)# 获取CPU使用率
cpu_percent = os.cpu_percent(interval=1)
print("CPU 使用率:", cpu_percent)

这段代码会输出当前系统的 CPU 逻辑个数、物理核心个数以及当前 CPU 的使用率。

总结

多线程——IO密集型,例如文件下载
多进程——CPU密集型,例如文件读写

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

频谱论文:基于空间稀疏采样的频谱态势生成: 模型与算法

#频谱# 张国勇,王军,陈霄南等.基于空间稀疏采样的频谱态势生成:模型与算法[J].中国科学:信息科学,2022,52(11):2011-2036. (电子科技大学) 摘要 面对日益复杂的电磁频谱环境和持续增长的用频需求, 为了维护电磁频谱秩序和安全, 提高频谱资源整体利用效率…

【Vue】Vue2的最后一舞

Vue 2.7 是 Vue 2.x 的最后一个次级版本。在此发布之后,Vue 2 将会进入长期技术支持 (LTS:long-term support) 状态,该状态从现在起计算会持续 18 个月,且不再提供新特性。 根据Vue官网的说法,随着Vue 2.7的发布&#…

《LeetCode力扣练习》代码随想录——栈与队列(逆波兰表达式求值---Java)

《LeetCode力扣练习》代码随想录——栈与队列&#xff08;逆波兰表达式求值—Java&#xff09; 刷题思路来源于 代码随想录 150. 逆波兰表达式求值 栈 class Solution {public int evalRPN(String[] tokens) {// Stack<Integer> stacknew Stack<>();Deque<Integ…

Vue - 事件处理详解

组件代码 下面是一个基本的 Vue.js 组件代码&#xff0c;其中包含了不同类型的事件处理器&#xff1a; <template><div><!-- 内联事件处理器&#xff1a;点击按钮时调用 increaseCount 方法 --><button click"increaseCount">加 1</but…

南大通用数据库 GBase 8a 性能调优方法--Hash索引

南大通用数据库--GBase 8a中建立Hash Index 通常可以用来解决等值查询的定位效率&#xff0c;特别是对以单表精确查询为主的应用场景尤为适合&#xff0c;如电信业务中的并发话单查询等&#xff08;特别是内存基本充足的场景&#xff09;。 默认创建GLOBAL的哈希索引。创建全局…

组合[中等]

一、题目 给定两个整数n和k&#xff0c;返回范围[1, n]中所有可能的k个数的组合。你可以按 任何顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;n 4, k 2 输出&#xff1a; [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ] 示例 2&#xff1a; 输入&#xff1a;n 1…

《对话品牌》——魅力女人:成就幸福人生

本期节目《对话品牌》栏目组邀请到了北京紫琪尔健康管理集团董事长紫琪尔孙红宇女士参加栏目录制&#xff0c;分享其企业故事&#xff0c;树立品牌形象&#xff0c;提升品牌价值&#xff01; 节目嘉宾&#xff1a;紫琪尔集团董事长孙红宇 节目主持人&#xff1a;吴昊 节目播…

前端 -- 基础 常用标签 ( 图像标签及其属性详解)

目录 图像标签 &#xff1a; 插入图像 图像标签的其它属性 &#xff1a; alt 属性 title 属性 width 属性 height 属性 border 属性 注意 图像标签 &#xff1a; 在我们的网页中&#xff0c;可以看到 好多 好多 图片&#xff0c;那这些图片又是怎样 在…

Android画布Canvas drawPath绘制跟随手指移动的圆,Kotlin

Android画布Canvas drawPath绘制跟随手指移动的圆&#xff0c;Kotlin import android.content.Context import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Path import android.os.Bundle import android.…

数据库中修改表的语句

1.修改表名&#xff1a;ALTER TABLE 旧表名 RENAME AS 新表名 例&#xff1a;ALTER TABLE teacher RENAME AS teacher1 2.增加表的字段&#xff1a;ALTER TABLE 表名 ADD 字段名 列属性 例&#xff1a;ALTER TABLE teacher1 ADD age INT(11) …

Java 类加载与字节码技术

3 类加载与字节码技术 3.1 类文件结构 类文件结构字节码指令编译期处理类加载阶段类加载器运行期优化 根据 JVM 规范&#xff0c;类文件结构如下 ClassFile {u4 magic;u2 minor_version; // 小版本号u2 major_version; // 主版本号u2 constant_pool_count; // 常量池cp_info…

算法时间空间复杂度计算—空间复杂度

算法时间空间复杂度计算—空间复杂度 空间复杂度定义影响空间复杂度的因素算法在运行过程中临时占用的存储空间讲解 计算方法例子1、空间算法的常数阶2、空间算法的线性阶&#xff08;递归算法&#xff09;3、二分查找分析方法一&#xff08;迭代法&#xff09;方法二&#xff…

WEB 3D技术 three.js 色彩空间讲解

上文 WEB 3D技术 three.js 设置环境贴图 高光贴图 场景设置 光照贴图 我们讲了基础材质的各种纹理 但是 我们的图片 到了界面场景中 好像绿的程度有点不太一样了 这里的话 涉及到我们的色彩空间 他有两种 一种是线性的 一种是 sRGB类型的 线性呢 就是根据光照强度 去均匀分…

【满分】【华为OD机试真题2023CD卷 JAVAJS】API集群负载统计

华为OD2023(C&D卷)机试题库全覆盖,刷题指南点这里 API集群负载统计 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: 某个产品的RESTful API集合部署在服务器集群的多个节点上,近期对客户端访问日志进行了采集,需要统计各个API的访问频次,根据热点信息在服务…

拓扑排序图解-Kahn算法和深度优先搜索

拓扑排序 是将一个有向无环图中的每个节点按照依赖关系进行排序。比如图 G G G存在边 < u , v > <u,v> <u,v> 代表 v v v的依赖 u u u, 那么在拓扑排序中&#xff0c;节点 u u u一定在 v v v的前面。 从另一个角度看&#xff0c;拓扑排序是一种图遍历&#…

KMP算法的理解+板子

对kmp算法的理解中&#xff0c;很重要的一点就是next数组。 很多人不理解next数组的含义&#xff0c;是因为它同时具有两个意思&#xff0c;而且这两个意思在不同的环境下不同。 现在给你两个字符串&#xff1a; 一个是文本串 text 一个是模板串 pattern 然后定义两个指针…

《别让猴子跳回背上》——管理者的时间管理

讲时间管理的书很多&#xff0c;但这本是专门讲给管理者的时间管理。 在职场中&#xff0c;许多管理者都会碰到工作计划执行不下去、组织目标难于实现的问题&#xff0c;搭进了自己所有可以支配的时间&#xff0c;仍旧是焦头烂额&#xff0c;顾此失彼&#xff1b;而下属则因为…

力扣题目学习笔记(OC + Swift)19. 删除链表的倒数第 N 个结点

19. 删除链表的倒数第 N 个结点 给你一个链表&#xff0c;删除链表的倒数第 n 个结点&#xff0c;并且返回链表的头结点。 此题目为链表题&#xff0c;拿出我们的杀手锏&#xff0c;链表解题经典三把斧&#xff1a; 哑巴节点栈快慢指针 关于内存问题&#xff1a;由于Swift及…

算法基础day1

归并排序模版 #include <iostream> using namespace std; int n; const int N 1e610; int q[N],tmp[N]; void merge_sort(int l,int r,int q[]){if(l>r) return;int mid lr>>1;merge_sort(l,mid,q);merge_sort(mid1,r,q);//归并的的过程int k0,il,jmid1;while(…

【Vue2+3入门到实战】(10)Vue基础之一文掌握 组件通信 详细示例(组件通信语法、父传子 、 子传父、非父子通信)

这里写自定义目录标题 一、学习目标1.组件通信2.综合案例&#xff1a;小黑记事本&#xff08;组件版&#xff09; 二、组件通信1.什么是组件通信&#xff1f;2.组件之间如何通信3.组件关系分类4.通信解决方案5.父子通信流程6.父向子通信代码示例7.子向父通信代码示例8.总结 三、…