python threading Python threading不是玩具!共享资源如虎添翼,但小心踩坑
python threading一、线程简介线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中,是进程中的实际运作单位。
一、线程简介
线程, 作为操作系统里能开展运算调度的最小单元, 它存在于进程里边, 是进程当中实际运行的单元。
线程作为操作系统直接予以支持的执行单元, 所以, 高级语言一般都在内部设置了对多线程的支持, 这也不例外, 而且, 其线程属于真正的Posix, 而非经由模拟产生的线程。
多条于同一进程里的线程, 会共享此进程里的全部系统资源, 像虚拟地址空间、文件描述符以及信号处理之类等等。不过同一进程中的多个线程, 有着各自的调用栈, 有着自己的寄存器环境, 有着自己的线程本地存储。每个线程自身的一组CPU寄存器, 称作线程的上下文, 该上下文所反映的是线程上次运行时该线程的CPU寄存器的状态。
虽然线程称得上是真正的线程, 然而在解释器执行代码之际, 存在着一个名为GIL锁的Lock, 任何线程在执行之前, 都必定要先获取GIL锁, 之后, 每执行100条字节码, 解释器便会自动释放GIL锁, 从而给予别的线程得以执行的机会。事实上, 这个GIL全局锁将所有线程的执行代码都施加了锁, 所以多线程在其中仅仅能够交替执行, 不管存在多少个线程, 也仅仅只能使用到1个核。设计方面, 是借助多进程达成多核任务, 要是打算同时去运行多个有关 I/O 密集型的任务, 那么多线程依旧是一种恰当的模型。
标准库给出了两个模块, 分别是和, 其中是低级的模块, 而则是高级模块, 此高级模块针对进行了封装。在绝大多数平常状况下, 我们仅仅只需用到这个高级别的模块便可。开启一个线程, 便是将一个函数传进去并且创建实例, 接着再调用start()来开始展开执行。
鉴于任何进程在默认情况下就会开启一个线程, 我们将这个线程称作主线程, 主线程能够启动新的线程, 有个()函数的模块, 它始终返回当前线程的实例, 主线程实例的名称是, 子线程的名字在创建之际指定。
二、多线程编程实例
2.1 语法
, 你提供的这个内容似乎不太清晰准确, 不太能按照要求进行改写。你可以检查一下并准确清晰地表述相关内容, 以便我能更好地为你完成改写。
应当始终使用关键字参数调用此构造函数。 参数如下:
组: 应当是无,留作供将来去实现 类的延展来使用。
是那种被应用于run()方法调用的、具备可调用特性的对象, 默认呈现为None这种形态, 这意味着并不需要去调用任何方法。
名为, 是线程的名称。在默认的情形之下, 会以“-N”这样的形式去构造唯一的名称, 其中N是一个较小的十进制数值, 或者是“-N ()”的形式, 其中“”为., 要是指定了参数的话。
args, 它是那种用于发起调用目标函数的参数列表或者元组, 其默认的情况是 ()。
: 是用于调用目标函数的关键字参数字典。默认是 {}。
要是并非None这类情况, 参数会明确地去设定该线程是不是守护模式。要是是None(即为默认值时), 线程会承继当前线程的守护模式特性。
倘若子类型对构造函数进行了重载, 那么它必须得保证, 在开展任何事情之前, 要先去发起调用基类构造器(.())。
一个用于表明某个线程究竟是不是守护线程的布尔值, 若是守护线程就为 True, 若不是则为 False。此值必须要于调用 start() 之前进行设置, 不然就会引发相应结果。它的初始值源自创建该线程的情况 ;主线程并非守护线程, 所以所有在主线程里创建的线程默认情况下那个表示是否为守护线程的值就等于 False。
2.2 方法
开启线程的活动, 它于某个线程之内, 顶多数目为一次地被予以调用, 它将对象的run()方法, 规划于一个单独的控制线程里去, 实行调用之举, 要是在同一个线程对象当中, 针对这个方法的调用次数, 比一次还要多, 那么就会抛出。
对应线程活动的办法, 你能够于子类型之中去重新定义这个办法, 标准的运行方法会针对当作参数传递给该对象构建器的可实现调用的对象(要是存在的话)展开调用, 并且会一同附加从位置参数集合和关键字参数集合各自获取到的位置参数以及关键字参数, 借助列表或者元组视作传递给位置参数集合之一的参数能够实现相同的结果。
进入等待状态, 直至线程结束。此操作将会使调用该方法的线程产生阻塞, 一直持续到被调用join() 的线程终结。这种终结情况无论是因正常结束, 抑或是于引发未处理异常时发生, 又或者是直至出现超时状况方可结束, 其中超时选项属于可选择的状态。
当那参数存在着并且并非是None的时候, 它理应是一个用来指定操作超时的以秒作为单位的浮点数或者分数。由于join()始终返回的是None, 所以你必定要在join()之后调用()才能够判断是不是发生了超时。要是线程依旧存活着, 那么join()则会超时。
要是参数不存在, 或者呈现 None 的状态, 这样子的操作就会出现阻塞情况, 一直持续到线程终结。
一条线程能够被合并好多回。要是试着加入当下线程会致使死锁, join()会引发异常。要是试着join()一个还未启动的线程, 同样会抛出一样的异常。
来返回一下线程是不是处于存活状态, 从run() 方法刚开始一直到run() 方法刚结束这个时间段内, 此方法返回的是True呢, 通过模块函数 () 能够返回包含所有处于存活状态线程的列表。
2.3 定时器对象
用来表明一个操作应当在经受一定时长的等待过后才去运行, 这等同于一个定时器, Timer类乃是某类的子类, 所以能够如同一个自定义线程那样开展工作。
像线程那般, 定时器同样是借助调用其Timer.start方式予以启动的 , 定时器能够经由调用()方法达成停止(在其动作起始之前), 定时器于执行其行动之前所需等待的时间间隔或许跟用户所指定的时间间隔并非全然一样。
建造一个定时器, 当历经了秒的间隔事件过后, 便会凭借参数args以及关键字参数去调用。要是args是None(此为默认值), 那么就会运用一个空列表。要是 为None(这是默认值), 那就会采用一个空字典。
将定时器予以停止, 把计时器即将去执行的操作予以取消。此情况仅仅是在计时器依旧处于等待状态之际方才具备效力。
2.4 进度条编程示例
2.4.1 源码
#-*- coding: UTF-8 -*-
import threading
import sys
import time
import os
#常数定义
PROGRESS_BAR_LENGTH = 101
#全局变量定义
COUNT = 1
class ThreadProgressBar (threading.Thread):
def __init__(self, name):
threading.Thread.__init__(self)
self.name = name
def run(self):
print ("\n开始线程:" + self.name)
normal_progress_bar()
print ("\n退出线程:" + self.name)
def normal_progress_bar():
global COUNT
start_time = time.time()
while(True):
COUNT = COUNT + 1
#进度100时退出
if COUNT >= PROGRESS_BAR_LENGTH:
break
end_time = time.time()
#\r 表示将光标的位置回退到本行的开头位置,重新打印
print("\r", end="")
#带百分比进度,打印方块
print("Download progress: {}%: ".format(COUNT), "▋" * (COUNT // 2),'[{:.2f}s]'.format(end_time-start_time), end="")
sys.stdout.flush()
#模拟IO读写时间
time.sleep(0.05)
if __name__ == '__main__':
#打印一个语
#正常处理,初始化全局变量
COUNT = 1
print("正常,执行进度演示".center(PROGRESS_BAR_LENGTH // 2,"-"))
normal_progress_bar()
#打印一个语
print('')
print("多线程,执行进度演示".center(PROGRESS_BAR_LENGTH // 2,"*"))
#多线程处理,初始化全局变量
COUNT = 1
# 创建新线程
threadList = []
for i in range(os.cpu_count()):
t = ThreadProgressBar(i)
t.start()
threadList.append(t)
for t in threadList:
t.join()
2.执行4.2的验证, 其中单线程执行的情况下时间是4.99秒, 而多线程执行时时间为0.31秒。
- /:
##山狗学会 Start##
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐

所有评论(0)