写给Python社群的第11课:Python线程,进程,协程,3个毫无关系的兄弟

news2024/11/23 3:24:36

文章目录

    • ⛳️ 线程、进程与协程
      • 🔥 进程与线程简介
    • ⛳️ Python 多线程模块
      • 🔥 threading 模块
      • 🔥 threading 模块实践
    • ⛳️ Python 并发进程模块
      • 🔥 Process 创建多进程

⛳️ 线程、进程与协程

线程、进程、协程 这三个名称相似的概念,都是为了让程序处理多个任务,从而加快任务处理效率,本篇博客就带大家掌握这三个知识点,让我们先从线程和进程的概念讲起。

🔥 进程与线程简介

进程是在计算机内存中运行的一个软件实例,它可以包含线程,在 Windows 电脑桌面任务栏右键选择任务管理器即可查看电脑进程,如下所示。
在这里插入图片描述
线程是程序执行流程的最小单元,它是进程的一部分,一个进程可以包含若干线程

所以在后续的学习中,一定要牢记,进程包含线程 这一大小关系。

下面我们先从多线程开始学习。

⛳️ Python 多线程模块

在 Python3.0 之后的版本中多线程模块常用的有 2 个,其一是 threading,其二是 queue模块,依次为大家介绍。

🔥 threading 模块

学习任何一个模块,都要掌握其提供的函数与类,当然有的模块还会提供常量,下面是 threading 模块提供的函数。

  • active_count():获取当前活动线程对象的数量;
  • current_thread():获取当前线程对象;
  • main_thread():获取主线程对象,主线程一般是 Python 解释器启动的线程;
  • get_ident():返回当前线程的 ID;
  • enumerate():获取当前所有活动的线程对象;

函数我们先稍微搁置,稍后查看使用代码,下面从 threading 模块提供的类开始学习。

threading 模块提供的类有 ThreadLockRLockConditionSemaphore

Thread 类
Thread 类是 threading 模块中最常用也最先学习的类,它通过调用用户自定义函数,生成一个独立的活动线程,这里用户自定义函数有两种形式,分别如下。

  • 在使用 Thread 创建实例对象时,将自定义函数以参数形式传递给构造函数;
  • 通过继承 Thread 类,然后重新 run() 方法,调用用户自定义函数,需要注意在 Thread 类的派生类中,只允许重写 __init__() 构造方法和 run() 方法。

Thread 类的构造函数如下所示:

__init__(self, group=None, target=None, name=None, args=(), kwargs=None, *,daemon=None)

参数表描述如下。

  • group:无用占位参数,未来获取扩展功能使用;
  • target:创建的线程要调度的目标方法/函数;
  • name:线程名称,不指定默认生成规则是 Thread-N
  • args:如果自定义函数有参数时,通过该参数以元组形式传递
  • kwargs:同上,只是参数以 字典形式 传递;
  • daemon:创建的线程是否为守护线程,守护线程可以简单理解为当主线程退出时,守护线程会同步终止。

在初学阶段,重点掌握 targetargskwargs 三个参数即可。

Thread 类的主要方法如下

  • start():线程启动状态,该方法要求在 run() 方法前被调用;
  • run():运行线程;
  • join():阻塞线程,等待调用该方法的线程对象运行完毕;
  • name:线程名称;
  • ident:线程 ID;
  • is_alive():判定线程是否活动;
  • daemon:查看线程是否为守护线程。

上述各方法执行的简单顺序为,线程对象创建之后,首先调用 start() 方法,进行启动,然后调用 run() 运行线程绑定的函数,join() 方法可以阻塞当前运行线程。

Lock 类和 RLock 类
Lock 类是原始锁,RLock 类是课重复锁,原始锁可以提供锁定解锁两种状态,一旦线程获得锁之后,相应请求就会被阻塞,直到它被释放,建立一个锁的方法是 acquire(),释放锁的方法是 release()。RLock 类对象可以被一个线程多次获取,防止出现 Lock 锁的死锁问题,同样提供两个方法,分别是 acquire()release()

Condition 类和 Semaphore 类
Condition 类是条件变量对象,它提供对复杂线程同步问题的解决方案,除了与 Lock 类对象一样的 acquire()release() 方法外,还提供了 wait()notify() 方法。

Semaphore 信号量用于管理内部数据其,每次当线程使用 acquire() 方法后,计数器+1,使用 release() 方法后计数器-1。

🔥 threading 模块实践

本小节使用一个案例实现多线程应用,场景模拟书店售书系统,假设书店库存如下所示。

书籍名称库存数量
《滚雪球学 Python》20
《爬虫 100 例》10
《Python 爬虫 120》100
《写给 Python 社群的课》5
《数据结构》15
《运筹学》0

来了 3 名学生购买图书,详情如下所示。

  • A 同学,购买《滚雪球学 Python》 3 本;
  • B 同学,购买 《写给 Python 社群的课》 2 本;
  • C 同学,购买 《数据结构》5 本。

他们同时将书给了 3 名售卖员(这里假设有多个结算通道,不排队)。

下面实现相应的代码,注意代码不用多线程技术实现。

import time
from datetime import datetime

books = [
    ['《滚雪球学Python》', 20],
    ['《爬虫100例》', 10],
    ['《Python爬虫120》', 100],
    ['《爬虫100例》', 10],
    ['《写给Python社群的课》', 5],
    ['《数据结构》', 15],
    ['《运筹学》', 0]
]


def buy_book(name, num):
    # 每次购书,模拟消耗时间 1 秒
    time.sleep(1)

    for index, item in enumerate(books):

        if item[0] == name:
            if item[1] >= num:
                books[index][1] = item[1] - num
                return num
            else:
                print(f"{name} 库存不足,无法购买")
                return -1


if __name__ == '__main__':
    print("开始购买", datetime.now())
    # A 同学开始购书
    ret = buy_book('《滚雪球学Python》', 3)
    if ret > 0:
        print("A 购买成功")

    # B 同学开始购书
    ret = buy_book('《写给Python社群的课》', 2)
    if ret > 0:
        print("B 购买成功")

    # C 同学开始购书
    ret = buy_book('《数据结构》', 5)
    if ret > 0:
        print("C 购买成功")

    print("三人结束购买", datetime.now())

    # 图书剩余数量
    for b in books:
        print(b)

运行代码,可以看到输出如下,附带模拟消耗时间输出,累计消耗 4 秒左右。

开始购买 2022-12-19 11:54:33.993924
A 购买成功
B 购买成功
C 购买成功
三人结束购买 2022-12-19 11:54:37.034426
['《滚雪球学Python》', 17]
['《爬虫100例》', 10]
['《Python爬虫120》', 100]
['《爬虫100例》', 10]
['《写给Python社群的课》', 3]
['《数据结构》', 10]
['《运筹学》', 0]

上述代码三名同学依次购买,完全模拟单人排队场景,显然这与我们的目标不一致,毕竟刚刚前文安排了 3 名售卖员,下面对代码进行修改,使用 threading 函数实现多线程案例。

多线程代码实现如下所示

import time
from datetime import datetime
import threading

books = [
    ['《滚雪球学Python》', 20],
    ['《爬虫100例》', 10],
    ['《Python爬虫120》', 100],
    ['《爬虫100例》', 10],
    ['《写给Python社群的课》', 5],
    ['《数据结构》', 15],
    ['《运筹学》', 0]
]


def buy_book(name, num):
    # 每次购书,模拟消耗时间 1 秒
    time.sleep(1)

    for index, item in enumerate(books):

        if item[0] == name:
            if item[1] >= num:
                books[index][1] = item[1] - num
                return num
            else:
                print(f"{name} 库存不足,无法购买")
                return -1


if __name__ == '__main__':
    print("开始购买", datetime.now())

    t1 = threading.Thread(target=buy_book, args=('《滚雪球学Python》', 3))  # A 同学开始购书
    t2 = threading.Thread(target=buy_book, args=('《写给Python社群的课》', 2))  # B 同学开始购书
    t3 = threading.Thread(target=buy_book, args=('《数据结构》', 5))  # C 同学开始购书
    t1.start()  # 线程启动
    t2.start()
    t3.start()

    t1.join()  # 等待线程结束
    t2.join()
    t3.join()

    print("三人结束购买", datetime.now())

    # 图书剩余数量
    for b in books:
        print(b)

运行上述代码,能看到购书时间大幅度缩短,用了 1 秒左右的时间。

开始购买 2022-12-19 12:02:57.963400
三人结束购买 2022-12-19 12:02:58.973167
['《滚雪球学Python》', 17]
['《爬虫100例》', 10]
['《Python爬虫120》', 100]
['《爬虫100例》', 10]
['《写给Python社群的课》', 3]
['《数据结构》', 10]
['《运筹学》', 0]

在上述代码中,我们通过 3 个 start() 方法启动了 3 个进程运行,然后使用 3 个 join() 等待进程运行完毕,并且可以明显看到运行时间变快,这里要注意不同的电脑得到的结果可能不一致,这是计算机本身问题,所以多线程应用开发完毕,需要尽可能多的进行测试。

除了上述写法外,还可以使用 threading 类实现。

import time
from datetime import datetime
import threading

books = [
    ['《滚雪球学Python》', 20],
    ['《爬虫100例》', 10],
    ['《Python爬虫120》', 100],
    ['《爬虫100例》', 10],
    ['《写给Python社群的课》', 5],
    ['《数据结构》', 15],
    ['《运筹学》', 0]
]


def buy_book(name, num):
    # 每次购书,模拟消耗时间 1 秒
    time.sleep(1)

    for index, item in enumerate(books):

        if item[0] == name:
            if item[1] >= num:
                books[index][1] = item[1] - num
                return num
            else:
                print(f"{name} 库存不足,无法购买")
                return -1


# 定义一个类,并让其继承自 Thread
class BookThread(threading.Thread):
    def __init__(self, target, args):
        threading.Thread.__init__(self)
        self.target = target
        self.args = args

    # 重写 run 方法
    def run(self):
        self.target(*self.args)


if __name__ == '__main__':
    print("开始购买", datetime.now())

    # 三人购买数据
    purchasers = [
        ('《滚雪球学Python》', 3),
        ('《写给Python社群的课》', 2),
        ('《数据结构》', 5)
    ]

    # 待指定的任务清单
    do_list = []

    # 将进程类添加到列表中
    for one in purchasers:
        item = BookThread(target=buy_book, args=one)
        do_list.append(item)

    for i in range(len(do_list)):
        do_list[i].start()  # 启动线程

    for i in range(len(do_list)):
        do_list[i].join()  # 等待线程运行完毕

    print("三人结束购买", datetime.now())

    # 图书剩余数量
    for b in books:
        print(b)

上述代码的实现结果与前文一致,仅写法上与前文有所差异,即使用面向对象写法实现,学习的时候可以反复临摹几遍。

⛳️ Python 并发进程模块

在 Python 中 multiprocessing 是一个支持多进程的软件包(模块),它可以在 UNIX 和 Windows 上运行,多进程更适合处理 CPU 密集型任务,如果是 I/O 密集型任务,依旧建议使用多线程处理。

🔥 Process 创建多进程

mulitprocessing 模块最主要的对象是 Process 类,可以使用它来创建进程。其构造函数如下所示:

class multiprocessing.Process(group=None, target=None, name=None, args=(), kwargs={})

其中:

  • group 参数:可以用于指定进程组,但是通常不使用。
  • target 参数:是一个可调用对象,它指定新进程要执行的函数。
  • name参数:是一个字符串,可以用于为新进程命名。
  • args:是一个元组,包含传递给函数的参数。
  • kwargs:是一个字典,包含传递给函数的关键字参数。

下面是一个简单的例子,展示如何使用 Process 类创建新的进程:

import multiprocessing

def process_function(arg1, arg2):
    print(arg1, arg2)


if __name__ == '__main__':

    p = multiprocessing.Process(target=process_function, args=(10, 20))
    p.start()
    p.join()

在这个例子中,我们创建了一个新的进程,并将 process_function 函数作为目标传递给了进程。这个函数接受两个参数,分别是 1020。我们调用 start() 方法来启动进程,然后调用 join() 方法来等待进程完成。

请注意,在 Python 3.8 及更高版本中,multiprocessing 模块的默认导入名称已经更改为 multiprocessing,而不是 iprocessing

Process 类所支持的方法和属性

  • start() 方法:用于启动新进程。
  • join([timeout]) 方法:用于等待新进程完成,并返回其退出状态。如果提供了 timeout 参数,则等待指定的时间(以秒为单位),然后返回。
  • is_alive() 方法:用于检查新进程是否在运行。
  • pid 属性:是一个整数,表示新进程的进程 ID。
  • name 属性:是一个字符串,表示新进程的名称。
  • daemon 属性:是一个布尔值,表示新进程是否是守护进程。

下面是一个简单的例子,展示了如何使用 Process 类的几个方法和属性:

import multiprocessing
import time


def process_function(arg1, arg2):
    print(arg1, arg2)
    time.sleep(5)


if __name__ == '__main__':
    p = multiprocessing.Process(target=process_function, args=(10, 20))
    print("Process ID:", p.pid)
    print("Process Name:", p.name)
    print("Process Daemon:", p.daemon)

    p.start()
    print("Process is alive:", p.is_alive())

    p.join(1)
    print("Process is alive:", p.is_alive())

    p.join()
    print("Process is alive:", p.is_alive())

在这个例子中,我们创建了一个新的进程,并输出了它的进程 ID、名称和是否是守护进程。然后,我们启动进程并使用 is_alive() 方法检查它是否正在运行。接下来,我们调用 join() 方法两次,第一次等待 1 秒,第二次等待直到进程完成。最后,我们再次使用 is_alive() 方法检查进程是否正在运行。

运行这个例子的输出如下所示:

Process ID: None
Process Name: Process-1
Process Daemon: False
Process is alive: True
10 20

📢📢📢📢📢📢
💗 你正在阅读 【梦想橡皮擦】 的博客
👍 阅读完毕,可以点点小手赞一下
🌻 发现错误,直接评论区中指正吧
📆 橡皮擦的第 792 篇原创博客

从订购之日起,案例 5 年内保证更新

  • ⭐️ Python 爬虫 120,点击订购 ⭐️
  • ⭐️ 爬虫 100 例教程,点击订购 ⭐️

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

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

相关文章

大话设计模型 Task03:工厂、制造、观察

目录一、建造者模式问题描述问题分析模式定义代码实现二、观察者模式问题描述问题分析模式定义代码实现一、建造者模式 问题描述 我的要求是你用程序画一个小人,这在游戏程序里非常常见,现在简单一点,要求是小人要有头、身体、两手、两脚就可…

左偏树原理详解

一 点睛 左偏树(leftist tree 或 leftist heap)也叫作左偏堆、左倾堆、左式堆,是计算机科学中的一种树,也是一种优先队列实现方式,属于可并堆,在信息学中十分常见,在统计、最值、模拟、贪心等类…

Win11安装WSL2和Nvidia驱动(2022-12-19)

文章目录前言系统环境WSL 1和WSL 2功能对比安装WSL2更新和升级包配置VSCode配置GPU加速安装Nvidia驱动安装Cuda Toolkit通过PyTorch安装CUDA Toolkit测试Nvcc参考链接前言 以前捣鼓过wsl,即Windows下的Linux子系统,但兼容性依然比不过原生的Linux系统&a…

【Linux】进程间通信之管道

目录🌈前言🌸1、IPC介绍🍢1.1、进程间通信的目的🍡1.2、背景和发展🍠1.3、进程间通信的分类🌷2、管道🍡2.1、概念🍢2.2、管道的原理🍣2.3、匿名管道🍤2.4、管…

DOM算法系列004-判断给定节点是否为body元素

UID: 20221218221939 aliases: tags: source: cssclass: created: 2022-12-18 如果我们要判定给定的一个节点是不是页面body与元素节点,该如何判断呢? 一般来说, 一个HTML页面内只有一个body元素 但是,如果我们非要在页面内写超过…

Spring boot 整合 redis

Spring boot 整合 redis一、Spring boot 整合redis1.1 启动redis1.2 redis desktop manager1.3 常用命令二、操作2.1 依赖包2.2 配置2.3 简单测试2.4 StringRedisTemplate一、Spring boot 整合redis 1.1 启动redis 命令行启动 redis-server redis-cli1.2 redis desktop mana…

基于electronbot作品bootLoader设计

文章目录 前言 一、芯片程序区规划和流程 1、flash区规划 2、两区运行流程 3、bootLoader代码体现 4、electronbot代码体现: 二、bootLoader代码设计 1.下载程序步骤 2.通讯协议格式 三、libusb开发及需要注意的事情 1、bootLoader复合设备 2、electronbot复合设备…

基础算法系列--[基本数据结构KMP]

文章目录前言链表单链表双链表栈和队列栈队列单调KMP前言 今天要搞的是基本的一些数据结构,当然咱们这个不是那么“正经”。当然今天也没啥代码,因为太简单了(其实我也想水一下~) 链表 单链表 单链表这个东西,应该…

Prometheus+Grafana

K8S prometheusK8S1.Prometheus(普罗米修斯)2.Prometheus可以做什么3.Prometheus的特点4.prometheus 相关组件二、prometheus与zabbix的区别zabbix架构区别三、prometheus架构分析1.TSDB2.时间序列数据库的特点3.prometheus 相关组件1.prometheus 核心组…

【计算机网络】实验五 网络层与链路层协议分析(PacketTracer)

一、实验目的 通过本实验,进一步熟悉PacketTracer的使用,学习路由器与交换机的基本配置,加深对网络层与链路层协议的理解。 二、实验内容: 4.1 路由器交换机的基本配置 打开下面的实验文件,按提示完成实验。 4.2…

直流微电网中潮流(Matlab代码实现)

目录 1 概述 1.1 直流电网中的潮流 1.2 创新点和相关工作 1.3 本文结构 2 数学/网络模型 2.1 主-从操作 2.2 孤岛运行 3 牛顿法 4 案例及Matlab代码实现 1 概述 潮流是一个非线性问题,需要用牛顿法求解具有恒定功率端子的直流微电网。本文提出了牛顿法在…

曙光来临!Nature终于发现了新冠特效药?或将彻底终结新冠时代!

百趣代谢组学文献分享:2022年即将过去,随着疫苗的全面接种和三年以来“动态清零”的坚持,我们在应对新冠病毒如潮水般的攻击中取得了阶段性成果。虽然大家陆陆续续投入到正常的工作生活中,但是我们都知道新冠并未被“打败”&#…

MySQL中这14个有用的小知识,快学起来吧

前言 我最近用MYSQL数据库挺多的,发现了一些非常有用的小玩意,今天拿出来分享到大家,希望对你会有所帮助。 1.group_concat 在我们平常的工作中,使用group by进行分组的场景,是非常多的。 比如想统计出用户表中&…

如何在产品开发中讨论概念设计?

每当你看到一辆在路上行驶的汽车、书桌上的笔记本电脑、工业包装生产线、医院设备、家用仪器和其他形式的概念设计创意产品会感到难以置信,这就是我们在产品开发中讨论概念设计的原因。 概念设计是一个尚未解决或到目前为止尚未令人满意的问题。这是一个深思熟虑的解…

研究区域制图 | 在 ArcGIS Pro中创建地图布局

研究区域制图 | 在 ArcGIS Pro中创建地图布局 数据准备 首先需要两个图层,一个是市区图层,一个是省行政区划图层,我这里以吉林省以及吉林省长春市为例 新建布局 选择横向A5即可 添加参考线 不知道你们知不知道这个功能,反正小…

kotlin协程笔记:Dispatchers

Kotlin 的 launch 会调用 startCoroutineCancellable(),接着又会调用 createCoroutineUnintercepted(),最终会调用编译器帮我们生成 SuspendLambda 实现类当中的 create() 方法。 public fun CoroutineScope.launch(context: CoroutineContext EmptyC…

【JVM】本地方法栈与堆与方法区

文章目录1. 本地方法栈2. 堆3. 方法区1. 本地方法栈 本地方法栈和虚拟机栈有点类似,均具有线程隔离的特点以及都能抛出StackOverflowError和OutOfMemoryError异常。 但是不同之处在于本地方法栈服务的对象是JVM执行的native方法,而虚拟机栈服务的是JVM…

[附源码]Nodejs计算机毕业设计教师职称评定系统Express(程序+LW)

该项目含有源码、文档、程序、数据库、配套开发软件、软件安装教程。欢迎交流 项目运行 环境配置: Node.js Vscode Mysql5.7 HBuilderXNavicat11VueExpress。 项目技术: Express框架 Node.js Vue 等等组成,B/S模式 Vscode管理前后端分…

免费提供POSMV的GNSS数据解算服务,验潮仪丢失的一种补救

前两天有个网友问干活的区域附近是否有长期验潮站,因为他的临时验潮仪丢失了,随后问了一下搞水文的同事,他推了一个网址: http://publictide.nmdis.org.cn/tide?SiteGroup3&TideType0,中文名叫:潮汐潮…

C型利钠肽 ,101135-67-5

Bz-VGR-pNA, chromogenic substrate for trypsin and for bacterial trypsin-like proteases.Bz-VGR-pNA,胰蛋白酶和细菌胰蛋白酶样蛋白酶的显色底物。 编号: 127015中文名称: C型利钠肽 (TYR0)-C-PEPTIDE (DOG)英文名: (Tyr0)-C-Peptide (dog)CAS号: 101135-67-5单…