给程序提速 | 多进程与多线程

news2024/11/24 15:51:55

目录

一、背景

1.1、前言

1.2、说明

二、线程与进程

2.1、什么是进程

2.2、什么是线程

2.3、进程与线程的关系

2.4、多进程与多线程的最佳使用条件

2.5、线程与进程的锁

2.6、特别注意

三、第一个线程、线程池

3.1、线程测试

3.2、执行结果

3.3、线程池测试

3.4、执行结果

四、线程池批量下载美妞图片

4.1、步骤

4.2、完整代码

4.3、结果

五、一些线程函数

5.1、start()函数

5.2、join()函数

5.3、setDaemon()函数

5.4、Lock()函数、RLock()函数、acquire()函数与release()函数

六、总结


一、背景

1.1、前言

在爬虫获取资源的过程中,有时需要批量下载资源,但一次次的发起请求在下载速度实在太慢,需要提高获取资源的速度。那么线程与进程的知识就必不可少,下面对进程与线程进行学习并结合实战提高知识掌握程度。

1.2、说明

操作系统:win 10

编辑器:pycharm edu

语言及版本:python 3.10

使用的库:ThreadPoolExecutor、requests、BeautifulSoup

实现思路:写出单线程的代码,在把任务提交到线程池或者进程池即可

说明:下文不使用类的继承方法重写run函数,进行线程的运行

tips:以下代码url不会放真实的,移植测试注意识别更改

二、线程与进程

2.1、什么是进程

进程(运行中的程序)。每次执行一个程序,操作系统自动为该程序分配资源(如内存分配,创建一个可执行线程)。

2.2、什么是线程

线程(在运行中的程序内),是进程中的实际运作单位。可以直接被CPU调度的执行过程,是操作系统中可以进行运算调度的最小单位。

注意:不同进程之间的内存,默认不允许其它进程进行使用;

2.3、进程与线程的关系

进程是资源单位,线程是执行这些资源的运行单位。

小例子:进程是银行开设的某个业务,线程是办理这些业务的具体窗口。

关键词:资源单位、执行单位

2.4、多进程与多线程的最佳使用条件

1)什么时候使用多进程比较好?

计算密集型,使用多进程,如:大量的数据计算

各个部分相互独立,很少产生交集;

例子:代理池

1)获取到IP

2)验证IP是否可用

3)对外提供接口

以上三个步骤,相互独立,几乎不产生交集,使用多进程的效果更好

2)什么时候使用多线程比较好

I/O密集型,使用多线程,如:文件读写、网络数据传输(下载视频);

各个部分完成过程相似;

例子:批量获取图片、音乐、菜价等

1)获取内容url

2)发起请求

3)下载保存

以上的获取图片,音乐等步骤都是一致的,都是获取到该内容的url,然后对url发起请求,在下载保存,高度相似,使用多线程的效果更好。

2.5、线程与进程的锁

1)GIL锁

全局解释锁,是CPython解释器特有的,让一个进程中同一时刻只能有一个线程可以被CPU调用。(注意,此时如果有多个线程,那么CPU会轮流切换到不同的线程进行运行(不管当前线程是否结束),保证了线程的唯一运行性);

2)线程安全

单依靠线程的GIL锁数据是不安全的,所以要为线程申请锁,存在以下两种情况:

情况一:当多个线程同时申请一把锁时,第一个申请到锁的线程可以完整执行当前线程的程序,在释放所后;第二个申请到锁的线程继续执行当前线程的程序(此时其它没申请到锁的线程是阻塞状态);

情况二:多个线程申请不同的锁时,第一个申请到锁的线程开始执行,其它线程是阻塞状态,然后轮到第二个申请到锁的线程进行执行,直到全部执行结束;

3)死锁

出现原因:由于线程间竞争资源或者由于彼此通信而造成的一种阻塞现象;

结果:此时程序会卡着不动,一动不动;

例子一:模块1给申请了锁1,模块1申请了锁2,过了一会,模块1又跑去申请锁2(此时锁1、2都没有释放),或者模块2又跑去申请锁1,此时就会出现死锁。

2.6、特别注意

使用多线程、多进程、携程、进程池、线程池等方法提高程序运行速度时,需要考虑访问网站的承受能力,避免高速访问造成攻击。

三、第一个线程、线程池

3.1、线程测试

from threading import Thread  # 导入线程库


def function1(name):  # 函数1
    for i in range(1, 500):
        print(name, i)


def function2(name):  # 函数2
    for j in range(1, 500):
        print(name, j)


if __name__ == '__main__':
    # 创建线程并设置参数,target是执行的函数,args是传参并且传递是参数规定是元组,
    t1 = Thread(target=function1, args=("海绵宝宝",))  
    t2 = Thread(target=function2, args=("派大星",))
    t1.start()  # 开启线程
    t2.start()
    for k in range(1, 500):  
        print("我们去抓水母吧", k)

3.2、执行结果

可以看到多个函数并发运行输出结果,说明此方法可行。

如下图1:

 图1

3.3、线程池测试

from concurrent.futures import ThreadPoolExecutor  # 线程池库


def function(name):  # 测试函数
    print("我是", name)
    return name


def get_return(res):  # 拿返回结果函数
    print(res.result())


if __name__ == '__main__':
    with ThreadPoolExecutor(3) as t:  # 分配三个线程池,并且命名为t
        # 线程池t提交,添加返回绑定函数
        t.submit(function, "海绵宝宝").add_done_callback(get_return)
        t.submit(function, "派大星").add_done_callback(get_return)
        t.submit(function, "章鱼哥").add_done_callback(get_return)

3.4、执行结果

如下图2:

 图2

说明:由于进程与进程池的代码与线程的高度相似,只是导入的包不同,所以对该部分内容进行了跳过;

四、线程池批量下载美妞图片

4.1、步骤

1)请求到合集url

2)拿到每一张图片跳转url

3)拿到每一张图片下载地址

4)线程池执行

4.2、完整代码

代码需要注意的是:我们请求的url只有4个,线程池里的线程数量最好不要大于4,因为只有4个任务,你给多了线程也没啥用。

"""
    批量下载美妞图片实现思路
    1、

"""

from concurrent.futures import ThreadPoolExecutor
import requests
from bs4 import BeautifulSoup


def get_img_url():
    # all_url = "https://www.yeitu.com/meinv/siwameitui/2.html"

    for x in range(2, 6):
        url = "https://www.yeitu.com/meinv/siwameitui/{}.html".format(x)
        resp = requests.get(url, headers=header)
        resp.encoding = "utf-8"
        # print(resp.status_code)
        result = BeautifulSoup(resp.text, "html.parser")
        li_list = result.find("div", class_="list-box-p").find("ul").find_all("li")
        o = 0
        for i in li_list:
            src = i.find("a").get("href")
            two_resp = requests.get(src, headers=header)
            #  print(two_resp.status_code)
            two_resp.encoding = "utf-8"
            two_result = BeautifulSoup(two_resp.text, "html.parser")
            img_list = two_result.find("div", class_="img_box").find("a")

            for j in img_list:
                o += 1
                all_url = j.get("src")
                three_resp = requests.get(all_url, headers=header)
                name = all_url.split("/")[-1]
                #  print(three_resp.status_code)
                img_name = "{}".format(name)
                with open("tupian/"+img_name, mode="wb") as f:
                    f.write(three_resp.content)
                    print("图片{}下载完成".format(name))


if __name__ == '__main__':
    header = {
        'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:108.0) Gecko/20100101 Firefox/108.0'
    }
    with ThreadPoolExecutor(3) as t:
        t.submit(get_img_url)




4.3、结果

可以看到下载的图片整整齐齐的保存到了文件中,并且下载速度很快,这就是线程池的威力。

五、一些线程函数

5.1、start()函数

用法:线程对象.start()

含义:表示子线程已经准备好了,CPU可以开始调度该子线程

5.2、join()函数

用法:线程对象.join()

含义:等待该线程执行结束后,程序才往后继续执行

5.3、setDaemon()函数

用法:线程对象.setDaemon(布尔值),守护线程(必须放置start之前)

含义:布尔值为True,则设置为守护线程,主线程执行完毕后,子线程也自动关闭;

布尔值为False,则设置为非守护线程,主线程等待子线程,子线程执行完毕后,主线程才结束;

注意:非守护线程是默认值

5.4、Lock()函数、RLock()函数、acquire()函数与release()函数

用法:线程对象.Lock()、线程对象.RLock()、线程对象.acquire()、线程对象.release()

含义:创建同步锁、创建递归锁、申请锁、释放锁

当线程申请到了锁时,其它线程就会处于阻塞状态,等当前线程执行结束并释放锁后,由第二个申请到锁的线程执行,如此反复;

说明:RLock可以多次申请锁和多次释放、Lock不支持

六、总结

难点:

1)概念多;

2)方法多;

优点:

1)快速提高程序运行速度;

2)代码上手速度快;

总结:

1)多学习。

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

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

相关文章

华中科技大学计算机组成原理-计算机数据表示实验(全部通关)

计算机数据表示实验(HUST) 计算机数据表示目录 [建议收藏]计算机数据表示实验(HUST)第1关 汉字国标码转区位码实验第2关 汉字机内码获取实验第3关 偶校验编码设计第4关 偶校验解码电路设计第5关 16位海明编码电路设计第6关 16位海明解码电路设计第7关 海明编码流水传输实验第8关…

Leetcode:700. 二叉搜索树中的搜索(C++)

目录 问题描述: 实现代码与解析: 递归: 原理思路: 迭代: 原理思路: 问题描述: 给定二叉搜索树(BST)的根节点 root 和一个整数值 val。 你需要在 BST 中找到节点值…

CHAPTER 4 Docker仓库

docker仓库4.1 Docker Hub公共镜像市场4.2 第三方镜像市场4.2.1 daocloud4.2.2 阿里云4.3 *搭建本地私有仓库仓库(Repository)是集中存放镜像的地方,又分公共仓库和私有仓库。有时候容易把仓库与注册服务器(Registory)…

逆向-还原代码之continue (Interl 64)

// source code #include <stdio.h> int main() { int i; for (i 0; i < 10; i) { if (i 5) continue; printf("%d\n", i); } }

那年我双手离桌,被《剑指offer》打的还不了手(第八天)

跟着博主一起刷题 这里使用的是题库&#xff1a; https://leetcode.cn/problem-list/xb9nqhhg/?page1 目录剑指 Offer 55 - II. 平衡二叉树剑指 Offer 56 - I. 数组中数字出现的次数剑指 Offer 56 - II. 数组中数字出现的次数 II剑指 Offer 55 - II. 平衡二叉树 剑指 Offer 55…

缓存一致性问题解决方案(超全超易懂)

文章目录1、缓存模型和思路2、缓存更新策略3、两种解决方案3.1、先删除缓存&#xff0c;再更新数据库3.1.1延时双删&#xff08;解决先删除缓存&#xff0c;再更新数据库产生的缓存不一致问题&#xff09;1、什么是延时双删2、为什么要进行延迟双删&#xff1f;3、如何实现延迟…

【 uniapp - 黑马优购 | 购物车页面(2)】如何实现收货地址区域功能、常见问题解决方案

个人名片&#xff1a; &#x1f43c;作者简介&#xff1a;一名大二在校生&#xff0c;讨厌编程&#x1f38b; &#x1f43b;‍❄️个人主页&#x1f947;&#xff1a;小新爱学习. &#x1f43c;个人WeChat&#xff1a;见文末 &#x1f54a;️系列专栏&#xff1a;&#x1f5bc;…

JVM—类加载与字节码技术

目录一、类文件结构1、魔术2、版本3、常量池二、字节码指令1、javap工具2、图解方法执行流程3、通过字节码指令来分析问题4、构造方法5、方法调用6、多态原理——HSDB7、异常处理四、类加载阶段五、类加载器六、运行期优化一、类文件结构 以一个简单的HelloWord.java程序为例 …

聊聊VMware的三种网络模式

聊聊VMware的三种网络模式1.Bridged&#xff08;桥接模式&#xff09;2.NAT&#xff08;地址转换模式&#xff09;3.Host-Only&#xff08;仅主机模式&#xff09;VMware有三种虚拟网络工作方式&#xff0c;即&#xff1a; Briged&#xff08;桥接模式&#xff09;NAT&#xf…

实现内核线程

文章目录前言前置知识实验操作实验一实验二实验三前言 博客记录《操作系统真象还原》第九章实验的操作~ 实验环境&#xff1a;ubuntu18.04VMware &#xff0c; Bochs下载安装 实验内容&#xff1a; 在内核空间实现线程。实现双向链表。实现多线程在调度器的调度下轮流执行。…

【Nginx】Nginx配置实例-反向代理

1. 反向代理实例一 实现过程 1. 启动一个 tomcat&#xff0c;浏览器地址栏输入 127.0.0.1:8080&#xff0c;出现如下界面2. 通过修改本地 host 文件&#xff0c;将 www.123.com 映射到 127.0.0.13. 在 nginx.conf 配置文件中增加如下配置 2. 反向代理实例二 实现过程 1.准备两…

唤醒手腕 Go 语言学习笔记常见包 函数用法(更新中)

1. fmt 包 fmt 包的介绍&#xff1a;fmt format&#xff0c;是一种格式化输出函数汇总包&#xff0c;用于格式化输出。 Println、Print、Printf fmt.Print 原样输出 Print formats using the default formats for its operands and writes to standard output. start : ti…

Unity中的异步编程【5】——在Unity中使用 C#原生的异步(Task,await,async) - System.Threading.Tasks

一、UniTask&#xff08;Cysharp.Threading.Tasks&#xff09;和Task&#xff08;System.Threading.Tasks&#xff09;的区别 1、System.Threading.Tasks中的Task是.Net原生的异步和多线程包。2、UniTask(Cysharp.Threading.Tasks)是仿照.Net原生的Task&#xff0c;await&…

【C++】继承

​&#x1f320; 作者&#xff1a;阿亮joy. &#x1f386;专栏&#xff1a;《吃透西嘎嘎》 &#x1f387; 座右铭&#xff1a;每个优秀的人都有一段沉默的时光&#xff0c;那段时光是付出了很多努力却得不到结果的日子&#xff0c;我们把它叫做扎根 目录&#x1f449;继承的概…

通俗易懂的java设计模式(6)-代理模式

1.什么是代理模式 为某个对象提供一个代理对象&#xff0c;通过这个代理对象&#xff0c;可以控制对原对象的访问。 通俗的解释&#xff1a;我们电脑桌面上的一个个快接方式&#xff0c;当我们点击这个快捷方式的时候&#xff0c;我们就间接地访问到了这个程序。 2.静态代理…

附录B:Standard Delay Format(SDF)(下)

文章目录B.4 映射实例(Mapping Examples)传播延迟(Propagation Delay)输入建立时间(Input Setup Time)输入保持时间(Input Hold Time)输入建立和保持时间(Input Setup and Hold Time)输入恢复时间(Input Recovery Time)输入撤销时间(Input Removal Time)周期(Period)脉宽(Pulse…

C#自动化物流仓库WMS系统源码

分享一套C#自动化仓库WMS管理系统源码 MF00426 需要源码学习可私信我获取。 WMS作为现代物流系统中的主要组成部分&#xff0c;是一种多层次存放货物的高架仓库系统&#xff0c;由自动控制与管理系统、货架、巷道式堆垛机、出入库输送机等设 备构成&#xff0c;能按指令自动完…

PHP多进程(二)

上篇文章我们说到父进程应该回收子进程结束之后产生的数据,这样才会不浪费系统资源。 一个程序启动之后&#xff0c;变成了一个进程&#xff0c;进程在以下情况会退出 1&#xff09;运行到最后一行语句 2) 运行时遇到return 时 3) 运行时遇到exit()函数的时候 4) 程序异常的时…

【Dash搭建可视化网站】项目13:销售数据可视化大屏制作步骤详解

销售数据可视化大屏制作步骤详解1 项目效果图2 项目架构3 文件介绍和功能完善3.1 assets文件夹介绍3.2 app.py和index.py文件完善3.3 header.py文件完善3.4 api.py文件和api.ipynb文件完善3.4.1 需求数据获取3.4.2 header.py文件中数据变更3.5 middle.py文件完善3.5.1 中间第一…

24.数组名字取地址变成数组指针,数组名和指针变量的区别

数组名字取地址变成数组指针 1.一维数组名字取地址&#xff0c;变成一维数组指针&#xff0c;加1跳一个一维数组。 int a[10]; a1 跳一个整型元素&#xff0c;是a[1]的地址 a和a1相差一个元素&#xff0c;4个字节 &a 就变成了一个一位数组指针&#xff0c;是int(*p)[10]…