Python学习从0到1 day28 Python 高阶技巧 ⑤ 多线程

news2024/11/14 9:05:42

若事与愿违,请相信,上天自有安排,允许一切如其所是

                                                                                —— 24.11.12

一、进程、线程

现代操作系统比如Mac OS X,UNIX,Linux,Windows等,都是支持“多任务”的操作系统。

进程

进程:就是一个程序,运行在系统之上,那么便称之这个程序为一个运行进程,并分配进程ID方便系统管理。线程就是程序在系统中运行起来,被系统所管理的一个逻辑概念

进程就好比一家公司,是操作系统对程序进行运行管理的单位


线程

线程:线程是归属于进程的,一个进程可以开启多个线程,执行不同的工作,是进程实际工作的最小单位。

线程就好比公司的员工,进程可以有多个线程(员工),是进程实际的工作者

操作系统中可以运行多个进程,即多任务运行

一个进程内可以运行多个线程,即多线程运行


注意点:

进程之间是内存隔离的,即不同的进程拥有各自的内存空间。这就类似于不同的公司拥有不同的办公场所。

线程之间是内存共享的,线程是属于进程的,一个进程内的多个线程之间是共享这个进程所拥有的内存空间的。这就好比,公司员工之间是共享公司的办公场所。 


二、并行执行

并行执行的意思指的是同一时间做不同的工作

进程之间就是并行执行的,操作系统可以同时运行好多程序,这些程序都是在并行执行

除了进程外,线程其实也是可以并行执行的,也就是比如一个Python程序,其实是完全可以做到

        一个线程在输出:你好

        一个线程在输出:Hello

像这样一个程序在同一时间做两件乃至多件不同的事情,我们就称之为:多线程并行执行


三、threading模块

绝大多数编程语言,都允许多线程编程,Pyhton也不例外。

Python的多线程可以通过threading模块来实现。

语法

thread_obj = threading.Thread([group [, target [, name [, args [,  kwargs]]]]])

— self:在实例化对象并调用方法时会自动传入对象本身的引用。在这里表示当前正在被初始化的线程对象实例

— group:暂时无用,未来功能的预留参数

— target:执行的目标任务名

— name:线程名,一般不用设置

— args:以元组的方式给执行任务传参

— kwargs:以字典方式给执行任务传参

— daemon:用于设置线程是否为守护线程

守护线程是一种在后台运行的线程,当主线程结束时,守护线程通常也会随之结束(即使它自己的任务可能还没有完成)


示例

'''
演示多线程的使用
'''
import time
import threading

def sing():
    while True:
        print("唱歌ing")
        time.sleep(1)

def dance():
    while True:
        print("跳舞ing")
        time.sleep(1)

if __name__ == '__main__':
    sing_thread = threading.Thread(target=sing)
    dance_thread = threading.Thread(target=dance)
    # 启动线程
    sing_thread.start()
    dance_thread.start()


需要传参的话可以通过:

① args参数通过元组(按参数顺序)的方式传参

② 或使用kwargs参数用字典的形式传参

'''
演示多线程的使用
'''
import time
import threading

def sing(msg):
    while True:
        print(msg)
        time.sleep(1)

def dance(msg):
    while True:
        print(msg)
        time.sleep(1)

if __name__ == '__main__':
    # 以元组方式传参
    sing_thread = threading.Thread(target=sing,args=("我要唱歌",))
    # 以字典方式传参
    dance_thread = threading.Thread(target=dance,kwargs={"msg": "我要跳舞"})
    # 启动线程
    sing_thread.start()
    dance_thread.start()


四、多线程编程

1.threading模块的使用

thread_obj = threading.Thread(target=func)        创建线程对象
thread obj.start()        启动线程执行

2.如何传参

① 元组方式

    # 以元组方式传参
    sing_thread = threading.Thread(target=sing,args=("我要唱

② 字典方式

    # 以字典方式传参
    dance_thread = threading.Thread(target=dance,kwargs={"msg": "我要跳舞"}

3.线程类 threading

current_thread()    返回与Thread调用方的控制线程相对应的当前对象。
enumerate()            返回Thread当前所有活动对象的列表。
active_count()    返回Thread当前活动的对象数。返回的计数等于所返回列表的长度enumerate()。
main_thread()     返回主要Thread对象。在正常情况下,主线程是启动Python解释器的线程。
get_ident()      返回当前线程的“线程标识符”。这是一个非零整数。它的值没有直接的意义。


4.线程对象 threading.Thread()

守护线程

主线程不管守护线程的执行情况,只要是其他子线程结束且主线程执行完毕,主线程部会关闭。

常见方法

1、setDaemon(True)        可以把子线程设置为主线程的守护线程,此方法必须在start之前

2、join()        让主线程等待子线程执行,此方法在start之后

示例

"""
守护线程:
主线程不管守护线程的执行情况,只要是其他子线程结束且主线程执行完毕,主线程都会关闭。
常见方法:
1、setDaemon(True)方法可以把子线程设置为主线程的守护线程,此方法必须在start之前
2、join()方法,让主线程等待子线程执行,此方法在start之后
"""

import threading
import time


def run1(name, n):
    for _ in range(n):  # _下划线表示临时变量, 仅用一次,后面无需再用到
        print(name, time.ctime())
        time.sleep(1)


def run2(name, n):
    for _ in range(n):
        print(name, time.ctime())
        time.sleep(1)


if __name__ == '__main__':
    """
    设置子线程t1为守护线程,主线程不等t1运行结束,只要其他的子线程t2运行结果,就会关闭主线程。
    """
    t1 = threading.Thread(target=run1, args=("线程1", 10,))  # 注意args参数类型是元组并以“,”结尾
    t2 = threading.Thread(target=run2, args=("线程2", 5,))
    t1.setDaemon(True)  # 设置t1为守护线程
    t1.start()
    t2.start()

5.线程锁对象

acquire()        锁定        一旦线程获取了锁,随后的尝试将其阻塞,直到释放为止。任何线程都可以释放它。
release()        解锁

互斥锁 threading Lock

递归锁 threading RLock


6.条件对象 threading Condition

threading.condition 是 Python 标准库中用于线程间同步的一个高级机制。它将锁(Lock 或RLock )和条件变量组合在一起,使得线程可以在满足特定条件时等待,并且在条件满足时被唤醒。本质上,它提供了一种更灵活的线程间通信方式,基于共享资源的某种状态(条件)来控制线程的执行。

可以把Condiftion理解为一把高级的锁,它提供了比Lock,RLock更高级的功能,允许我们能够控制复杂的线程同步问题。

主要方法

① acquire 和 release 方法:

acquire 方法用于获取关联的锁。这和单独使用锁的操作类似,当一个线程调用 acquire 时,它会尝试获取锁。如果锁已经被其他线程持有,那么这个线程会被阻塞,直到锁被释放。

release 方法用于释放关联的锁,在完成对共享资源的操作后,线程应该释放锁,以便其他线程可以获取锁并访问共享资源。例如:

import threading
cond = threading.Condition()
cond.acquire()
try:
    # 在这里访问共享资源
finally:
    cond.release()
②  wait方法:

线程调用 wait 方法时,会释放当前持有的锁,并进入等待状态。这个线程会一直等待,直到被其他线程通过 notify 或 notify_all 方法唤醒。被唤醒后,线程会重新尝试获取锁,只有获取到锁后才能继续执行后续代码。

import threading
buffer = []
cond = threading.Condition()
def consumer():
    with cond:
        while not buffer:
            print("消费者:缓冲区为空,等待数据...")
            cond.wait()
        data = buffer.pop(0)
        print("消费者:消费了数据", data)
③ notify 和 notify_all 方法:

notify方法用于唤醒一个正在等待该条件变量的线程。在共享资源的状态发生改变,可能使得等待的线程能够继续执行时,应该调用这个方法来唤醒一个等待的线程。

notify_all方法则会唤醒所有正在等待该条件变量的线程。不过,只有在这些被唤醒的线程成功获取到锁后,才能真正继续执行。例如,生产者生产了新的数据后,可以调用 notify 方法唤醒等待的消费者线程:

import threading
buffer = []
cond = threading.Condition()
def producer():
    with cond:
        data = 1  # 假设生产的数据为1
        buffer.append(data)
        print("生产者:生产了数据", data)
        cond.notify()

7.信号量对象 threading Semaphole

Semaphore()        内部计数器

BoundedSemaphore()        继承Semaphore类,设定信号量边界值

"""
信号量(BoundedSemaphore类):
信号量通常用于保护容量有限的资源
信号量管理一个内部计数器,该内部计数器随每个acquire()调用而递减,并随每个 调用而递增release()。
计数器永远不能低于零。当acquire() 发现它为零时,它将阻塞,直到其他线程调用为止 release()。
"""

import threading, time

# 设置信号量的边界值
semaphore = threading.BoundedSemaphore(value=3)


def run():
    semaphore.acquire()  # 加锁
    print(threading.current_thread().getName(), time.ctime())
    time.sleep(5)
    semaphore.release()  # 释放


if __name__ == '__main__':
    for _ in range(10):
        t = threading.Thread(target=run)
        t.start()
    # 返回Thread当前所有活动对象的列表。通过运行结果我们可以看出系统是先一次性创建10个线程,然后根据信号量边界值3,一次性运行3个线程,其他线程等待锁。
    print(threading.enumerate())

"""
python多线程的缺陷:
1、GIL-全局解释器锁
2、无论系统CPU是几核的,只能使用一个来处理进程
"""

 


8.事件对象 threading Event

set()                  将事件对象的内部标志设置为 True 。这意味着等待这个事件的线程将会被唤醒并继续执行

import threading
event = threading.Event()
# 其他线程在等待这个事件
event.set()

clear()                  将事件对象的内部标志设置为 False 。如果在 clear 之后有线程调用 wait 方法,这些线程将会被阻塞,直到事件再次被设置。例如:

import threading
event = threading.Event()
event.set()
# 现在清除事件
event.clear()

wait(timeout=None)               如果“Flag"值为 False,那么当程序执行event.wait方法时就会阻塞;如果“Flag"值为True,那么执行event.wait方法时便不再阻塞

import threading
event = threading.Event()
def worker():
    print("线程开始等待事件")
    event.wait()
    print("事件已触发,线程继续执行")
t = threading.Thread(target=worker)
t.start()
# 在这里可以进行一些其他操作,然后设置事件
event.set()

is set()                 这是一个只读属性,用于检查事件对象的内部标志是否为 true 。可以用于在不阻塞线程的情况下检查事件的状态。例如:

import threading
event = threading.Event()
if not event.is_set():
    print("事件尚未设置")
event.set()
if event.is_set():
    print("事件已设置")
"""
事件对象 threading.Event
set()	将“Flag”设置为True
clear()	将“Flag”设置为False
wait()	如果“Flag”值为 False,主线程就会阻塞;如果“Flag”值为True,主线程不再阻塞。
isSet() 判断“Flag”值是否为True。
"""
import threading
import time

#实例化一个事件对象
event = threading.Event()

def run():
    while not event.isSet():
        print(threading.current_thread().getName(), time.ctime())
        time.sleep(5)
    event.wait(timeout=10)  # 阻塞线程,为什么timeout=10没起到作用

if __name__ == '__main__':
    #使用多线程去调用run
    for i in range(10):
        th = threading.Thread(target=run)
        th.start()
    #阻塞30s后运行主线程
    time.sleep(30)
    event.set()


9.Timer计时器对象 threading Timer

参数:(self, interval, function, args=None, kwargs=None)

start()        启动计时器

cancel()        停止计时器(在其动作开始之前)

"""
Timer对象
"""
import threading

# 实例化一个计时器
timer = threading.Timer


def hello():
    print("hello, world")


# 10s后执行hello
print("开始执行")
t = timer(10, hello)
t.cancel()
t.start()

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

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

相关文章

在Java中使用ModelMapper简化Shapefile属性转JavaBean实战

目录 前言 一、原始的处理办法 1、使用Set方法来转换 2、使用构造方法转换 二、基于ModelMapper的动态转换 1、ModelMapper简介 2、集成到项目中 3、Shapefile属性读取 三、总结 前言 在现代软件开发中,尤其是在多层架构中,经常需要将数据从一个…

2024版本IDEA创建Sprintboot项目下载依赖缓慢

目录 步骤一:在IDEA中搜索Maven(双击shift) 步骤二:找到Maven下的settings.xml文件修改镜像 ​编辑 ​编辑​编辑 步骤三:用VScode打开settings.xml文件修改镜像 ​编辑 步骤一:在IDEA中搜索Maven(双击shift) 步骤二&#xff…

【初阶数据结构与算法】链表刷题之移除链表元素、反转链表、找中间节点、合并有序链表、链表的回文结构

文章目录 一、移除链表元素思路一思路二 二、合并两个有序链表思路:优化: 三、反转链表思路一思路二 四、链表的中间节点思路一思路二 五、综合应用之链表的回文结构思路一:思路二: 一、移除链表元素 题目链接:https:…

三维测量与建模笔记 - 特征提取与匹配 - 4.1 梯度信息提取

上面说的“可关联性”,举一个例子,比如我们拍摄一个凹凸不平的金属表面,在某个角度拍的时候,从图像中可以看到这些凹凸不平的地方,但是换个角度(或者光照发生变化)拍就看不到了。这样的特征点就…

显示微服务间feign调用的日志

第一步 package com.niuniu.common.config;import com.niuniu.common.CommonConstant; import com.niuniu.common.utils.UserContext; import feign.Logger; import feign.RequestInterceptor; import feign.RequestTemplate; import org.springframework.context.annotation.…

读取文件内容、修改文件内容、识别文件夹目录(Web操作系统文件/文件夹详解)

前言 因 Unicode IDE 编辑器导入文件、文件夹需要,研究了下导入文件/文件夹的功能实现,发现目前相关文章有点少,故而记录下过程,如果有误,还望指正。(API的兼容性及相关属性、接口定义,请自行查看文件系统 …

「Mac玩转仓颉内测版2」入门篇2 - 编写第一个Cangjie程序

本篇详细介绍在Mac系统上创建首个Cangjie项目并编写、运行第一个Cangjie程序的全过程。内容涵盖项目创建、代码编写、程序运行与调试,以及代码修改后的重新运行。通过本篇,掌握Cangjie项目的基本操作,进一步巩固开发环境的配置,迈…

微信小程序_小程序视图与逻辑_day3

一、目标 A. 能够知道如何实现页面之间的导航跳转 B. 能够知道如何实现下拉刷新效果 C. 能够知道如何实现上拉加载更多效果 D. 能够知道小程序中常用的生命周期 二、目录 A. 页面导航 B. 页面事件 C. 生命周期 D. WXS脚本 E. 案例-本地生活(列表页面)…

Threejs 材质贴图、光照和投影详解

1. 材质和贴图 材质(Material)定义了物体表面的外观,包括颜色、光泽度、透明度等。贴图(Textures)是应用于材质的图像,它们可以增加物体表面的细节和真实感。 1.1材质类型 MeshBasicMaterial&#xff1a…

论文概览 |《Sustainable Cities and Society》2024.11 Vol.115(下)

本次给大家整理的是《Sustainable Cities and Society》杂志2024年11月第115期的论文的题目和摘要,一共包括76篇SCI论文!由于论文过多,我们将通过两篇文章进行介绍,本篇文章介绍第31--第76篇论文! 论文31 The effect of urban fo…

【问卷调研】HarmonyOS SDK开发者社区用户需求有奖调研

问卷请点击:HarmonyOS SDK开发者社区用户需求有奖调研

【3D Slicer】的小白入门使用指南五

心脏CT多组织半自动分割(解说版) 模块勾选的是converters下的crop volume 右侧移动滑块+移动边框,移动位置找到自己关注的区域即可,然后点击apply(其他参数也要设置的和图片一致) 模块勾选segment editor,并创建心脏多个不同分割位置(默认下不同位置自动分配了不同的颜…

丹摩征文活动|FLUX.1 和 ComfyUI:从部署到上手,轻松驾驭!

FLUX.1 和 ComfyUI:从部署到上手,轻松驾驭! FLUX.1历史曲线 黑森林实验室推出了一款名为FLUX.1的先进图像生成模型,根据不同用户需求,提供了三种独特的版本。 FLUX.1-pro:作为专为企业打造的强大闭源版本…

自动驾驶合集(更新中)

文章目录 车辆模型控制路径规划 车辆模型 车辆模型基础合集 控制 控制合集 路径规划 规划合集

string------1

文章目录 一. STL1.概念2.版本 二. string类2.1 为什么学习string类2. 标准库中的string类2.2.1 构造(7个)2.2.2 对string类对象进行“访问和修改”(1)operator[](2)迭代器1.迭代器的使用2.迭代器的价值&am…

开源 2 + 1 链动模式、AI 智能名片、S2B2C 商城小程序在用户留存与品牌发展中的应用研究

摘要:本文以企业和个人品牌发展中至关重要的用户留存问题为切入点,结合管理大师彼得德鲁克对于企业兴旺发达的观点,阐述了用户留存对品牌营收的关键意义。在此基础上,深入分析开源 2 1 链动模式、AI 智能名片、S2B2C 商城小程序在…

蓝桥杯每日真题 - 第8天

题目:(子2023) 题目描述(14届 C&C B组A题) 解题思路: 该代码通过动态计算包含数字 "2023" 的子序列出现次数。主要思路是: 拼接序列:将1到2023的所有数字按顺序拆分…

Mac Nginx 前端打包部署

安装homebrew /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)" 安装Nginx brew install nginx nginx相关命令 nginx启动命令:nginx nginx -s reload #重新加载配置 nginx -s reopen #重启 nginx -s stop #…

小试银河麒麟系统OCR软件

0 前言 今天在国产电脑上办公,需要从一些PDF文件中复制文字内容,但是这些PDF文件是图片转换生成的,不支持文字选择和复制,除了手工输入,我们还可以使用OCR。 1 什么是OCR OCR (Optical Character Recogni…

选择主动孤独,亦可以成长和放松

有的人热衷于喧嚣,享受来自社交场合的灯火辉煌,将欢声笑语作为心灵的慰藉。有的人则偏爱那份宁静,更愿意在青灯古巷间徘徊,在山川湖海间独行,以一杯茶、一卷书、一段旅程与自我对话。每个人以不同的方式诠释活着的意义…