python多线程:Thread类的用法

2023-11-10

我们要创建Thread对象,然后让他们运行,每个Thread对象代表一个线程,在每个线程中我们可以让程序处理不同的任务,这就是多线程编程。

创建Thread对象有两种方法:
1.直接创建Thread,将一个callable对象从类的构造器传递出去,这个callable就是回调函数,用来处理任务。
2.编写一个自定义类继承Thread,然后复写run()方法,在ru()方法中编写任务处理代码,然后创建Thread的子类。

1.直接创建Thread对象

# 导入Python标准库中的Thread模块 
from threading import Thread 
# 创建一个线程 
mthread = threading.Thread(target=function_name, args=(function_parameter1, function_parameterN)) 
# 启动刚刚创建的线程 
mthread .start()

参数说明:
function_name: 需要线程去执行的方法名
args: 线程执行方法接收的参数,该属性是一个元组,如果只有一个参数也需要在末尾加逗号

Thread类定义了以下常用方法与属性:

方法与属性 说明
方法与属性 说明
start() 启动线程,等待CPU调度
run() 线程被cpu调度后自动执行的方法
getName()、setName()和name 用于获取和设置线程的名称
setDaemon() 设置为后台线程或前台线程(默认是False,前台线程)。如果是后台线程,主线程执行过程中,后台线程也在进行,主线程执行完毕后,后台线程不论成功与否,均停止。如果是前台线程,主线程执行过程中,前台线程也在进行,主线程执行完毕后,等待前台线程执行完成后,程序才停止
ident 获取线程的标识符。线程标识符是一个非零整数,只有在调用了start()方法之后该属性才有效,否则它只返回None
is_alive() 判断线程是否是激活的(alive)。从调用start()方法启动线程,到run()方法执行完毕或遇到未处理异常而中断这段时间内,线程是激活的
isDaemon()方法和daemon属性 是否为守护线程
join([timeout]) 调用该方法将会使主调线程堵塞,直到被调用线程运行结束或超时。参数timeout是一个数值类型,表示超时时间,如果未提供该参数,那么主调线程将一直堵塞到被调线程结束

1)创建多线程
Thread的构造方法中,最重要的参数是target,所以我们需要将一个callable对象赋值给它,线程才能正常运行。
如果要让一个Thread对象启动,调用它的start()方法即可。

import threading
import time
def test():
    for i in range(5):
        print('The {} child thile '.format(i))
        time.sleep(1)
thread = threading.Thread(target=test)
thread.start()
# thread.join()

for i in range(5):
    print('The {} main thile '.format(i))
    time.sleep(1)


上面代码很简单,在主线程上打印 5 次,在一个子线程上打印 5 次。


The 0 child thile The 0 main thile 

The 1 child thile The 1 main thile 

The 2 child thile 
The 2 main thile 
The 3 main thile The 3 child thile 

The 4 child thile 
The 4 main thile 

上面的 callable 没有参数,如果需要传递参数的话,args 是固定参数,kwargs 是可变参数。

2)Thread的名字

每一个Thread都有一个name的属性,代表的就是线程的名字,这个可以在构造方法中赋值。如果在构造方法中没有为name赋值的话,默认就是“Thread-N”的形式,N是数字。

import threading
import time
def test():
    for i in range(5):
        print(threading.current_thread().name+" test ",i)
        time.sleep(1)
thread=threading.Thread(target=test)
thread.start()
for i in range(5):
    print("This is "+threading.current_thread().name+"main ",i)
    time.sleep(1)

输出为:

Thread-1 test This is MainThreadmain   00

Thread-1 test  1
This is MainThreadmain  1
Thread-1 test  2
This is MainThreadmain  2
This is MainThreadmain  3Thread-1 test 
 3
Thread-1 test  4
This is MainThreadmain  4

在Thread对象创建时,给name赋值

thread = threading.Thread(target=test,name='aoligei')

运行结果为:

aoligei test This is MainThreadmain   00

This is MainThreadmain  1
aoligei test  1
This is MainThreadmain  2
aoligei test  2
This is MainThreadmain  3
aoligei test  3
This is MainThreadmain  4
aoligei test  4

3)Thread的生命周期

1.创建对象时,代表Thread内部被初始化
2.调用start()方法后,thread会开始运行
3.thread代码正常运行结束或者遇到异常,线程会终止。
可以通过Thread的is_alive()方法查询线程是否还在运行。is_alive() 返回 True 的情况是 Thread 对象被正常初始化,start() 方法被调用,然后线程的代码还在正常运行。

import threading
import time
def test():
    for i in range(5):
        print(threading.current_thread().name+' test ',i,"*"*10)
        time.sleep(0.5)

thread = threading.Thread(target=test,name='aoligei')
thread.start()

for i in range(5):
    print(threading.current_thread().name+' main ', i)
    print(thread.name+' is alive ', thread.is_alive())
    time.sleep(1)

设置子线程的睡眠时间更短,可以让它早点结束

aoligei test MainThread main   00
 aoligei is alive ********** 
True
aoligei test  1 **********
MainThread main  1
aoligei is alive  True
aoligei test  2 **********
aoligei test  3 **********
MainThread main  2
aoligei is alive  True
aoligei test  4 **********
MainThread main  3
aoligei is alive  False
MainThread main  4
aoligei is alive  False

4)join()方法提供阻塞手段

import threading
import time
def test():
    for i in range(5):
        print(threading.current_thread().name+' test ',i,"*"*10)
        time.sleep(2)

thread = threading.Thread(target=test,name='aoligei')
thread.start()
thread.join()
for i in range(5):
    print(threading.current_thread().name+' main ', i)
    print(thread.name+' is alive ', thread.is_alive())
    time.sleep(1)

调用一个 Thread 的 join() 方法,可以阻塞自身所在的线程。主线程创建了 aoligei 对象后,让其 start,然后通过调用 join() 方法,实现等待。程序运行结果如下:

aoligei test  0 **********
aoligei test  1 **********
aoligei test  2 **********
aoligei test  3 **********
aoligei test  4 **********
MainThread main  0
aoligei is alive  False
MainThread main  1
aoligei is alive  False
MainThread main  2
aoligei is alive  False
MainThread main  3
aoligei is alive  False
MainThread main  4
aoligei is alive  False

默认情况下,join()会一直等待对应线程的结束,但可以通过参数赋值,等待规定的时间即可。

def join(self.timeout=None):

timeout是一个浮点参数,单位是秒。

5)守护线程

import threading
import time
def test():
    for i in range(5):
        print(threading.current_thread().name+' test ',i,"*"*10)
        time.sleep(2)

thread = threading.Thread(target=test,name='aoligei')
thread.start()

for i in range(5):
    print(threading.current_thread().name+' main ', i)
    print(thread.name+' is alive ', thread.is_alive())
    time.sleep(1)

输出为:

aoligei test MainThread main   00 
**********aoligei is alive 
 True
MainThread main  1
aoligei is alive  True
aoligei test  1 **********
MainThread main  2
aoligei is alive  True
MainThread main  3
aoligei is alive  True
aoligei test  2 **********
MainThread main  4
aoligei is alive  True
aoligei test  3 **********
aoligei test  4 **********

MainThread结束运行后,aoligei仍然在运行,这是因为MainThread在等待其他线程的结束。
aoligei的daemon属性默认为False,这使得 MainThread 需要等待它的结束,自身才结束。
如果要达到守护线程(MainThread 结束,子线程也立马结束),怎么做呢? 其实很简单,只需要在子线程调用 start() 方法之前设置 daemon 就好了。 当然也可以在子线程的构造器中传递 daemon 的值为 True。

thread = threading.Thread(target=test,name='TestThread',daemon=True)
# thread.setDaemon(True)

修改之后运行结果为:

aoligei test MainThread main   00 
aoligei is alive ********** 
True
MainThread main  1
aoligei is alive  True
aoligei test  1 **********
MainThread main  2
aoligei is alive  True
MainThread main  3
aoligei is alive  True
aoligei test  2 **********
MainThread main  4
aoligei is alive  True

6)线程数量
threading.enumerate():返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。

def enumerate():
“”"Return a list of all Thread objects currently alive.
The list includes daemonic threads, dummy thread objects created by current_thread(), and the main thread. It excludes terminated threads and threads that have not yet been started.

import threading
import time
def func1():
    print("This is func1")
    time.sleep(1)
def func2():
    print("This is func2")
    time.sleep(1)

for i in range(4):
    t1=threading.Thread(target=func1)
    t2 = threading.Thread(target=func2)
    t2.start()
    t1.start()
    le=len(threading.enumerate())
    print("当前线程数为",le)

运行结果为:

This is func2
This is func1当前线程数为
 3
This is func2
This is func1当前线程数为
 5
This is func2
This is func1当前线程数为
 7
This is func2
This is func1当前线程数为
 9

7)其他的方法
threading模块提供了一些比较实用的方法或者属性:

方法与属性 描述
current_thread() 返回当前线程
active_count() 返回当前活跃的线程数,1个主线程+n个子线程
get_ident() 返回当前线程
enumerate() 返回当前活动 Thread 对象列表
main_thread() 返回主 Thread 对象
settrace(func) 为所有线程设置一个 trace 函数
setprofile(func) 为所有线程设置一个 profile 函数
stack_size([size]) 返回新创建线程栈大小;或为后续创建的线程设定栈大小为 size
TIMEOUT_MAX Lock.acquire(), RLock.acquire(), Condition.wait() 允许的最大超时时间

2.自定义类继承Thread

自定义一个Thread的子类,然后复写它的run()方法

import threading
import time
class TestThread(threading.Thread):
    def __init__(self,name=None):
        threading.Thread.__init__(self,name=name)
    def run(self):
        for i in range(5):
            print(threading.current_thread().name+" test ",i)
            time.sleep(1)
thread=TestThread(name='aoligei')
thread.start()

for i in range(5):
    print(threading.current_thread().name+" main ",i)
    time.sleep(1)

自定义了 TestThread 这个类,然后继承threading.Thread 只有在 run() 方法中处理逻辑,最终运行结果如下:

aoligei test MainThread main   00

aoligei test MainThread main   1
1
MainThread main  2
aoligei test  2
aoligei test  3
MainThread main  3
MainThread main  4
aoligei test  4

3.GIL
全局解释器锁(global interpreter lock),每个线程在执行时候都需要先获取GIL,保证同一时刻只有一个线程可以执行代码,即同一时刻只有一个线程使用CPU,也就是说多线程并不是真正意义上的同时执行。
Python的标准实现是CPython。
CPython执行Python代码分为2个步骤:首先,将文本源码解释编译为字节码,然后再用一个解释器去 解释运行字节码。字节码解释器是有状态的,为了维护该状态的一致性,因此使用了GIL(Global Interpreter Lock,全局解释器锁)。 GIL的存在,使得CPython在执行多线程代码的时候,同一时刻只有一个线程在运行,无法利用多CPU 提高运算效率。但是这个特点也带来了一个好处:CPython运行多线程的时候,内部对象缺省就是线程 安全的。这个特性,被非常多的Python库开发者所依赖,直到CPython的开发者想要去除GIL的时候, 发现已经有大量的代码库重度依赖这个GIL带来的内部对象缺省就是线程安全的特性,变成一个无法解 决的问题了。
虽然多线程在并行计算场景下无法带来好处,但是在阻塞式IO场景下,却仍然可以起到提高效率的作 用。这是因为阻塞式IO场景下,线程在执行IO操作时并不需要占用CPU时间,此时阻塞IO的线程可以被 挂起的同时继续执行IO操作,而让出CPU时间给其他线程执行非IO操作。这样一来,多线程并行IO操作 就可以起到提高运行效率的作用了。
综上,Python的标准实现CPython,由于GIL的存在,同一个时刻只能运行一个线程,无法充分利用多 CPU提升运算效率,因此Python的多线程适用于阻塞式IO的场景,不适用于并行计算的场景

4.多进程和多线程对比
引自菜鸟教程:参考链接
如果多线程的进程是CPU密集型的,那多线程并不能有多少效率上的提升,相反还可能会因为线程的频繁切换,导致效率下降,推荐使用多进程;
如果是IO密集型,多线程进程可以利用IO阻塞等待时的空闲时间执行其他线程,提升效率。所以我们根据实验对比不同场景的效率

多线程在IO密集型的操作下似乎也没有很大的优势(也许IO操作的任务再繁重一些就能体现出优势),在CPU密集型的操作下明显地比单线程线性执行性能更差,但是对于网络请求这种忙等阻塞线程的操作,多线程的优势便非常显著了

多进程无论是在CPU密集型还是IO密集型以及网络请求密集型(经常发生线程阻塞的操作)中,都能体现出性能的优势。不过在类似网络请求密集型的操作上,与多线程相差无几,但却更占用CPU等资源,所以对于这种情况下,我们可以选择多线程来执行

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

python多线程:Thread类的用法 的相关文章

随机推荐

  • css flex布局 —— 容器属性 align-content

    align content 属性定义了多根轴线的对齐方式 如果项目只有一根轴线 该属性不起作用 如果只有一根轴线 align content 几乎等同于 align items 容器属性 align content 生效的条件是 必须显式的
  • 高校校园网使用的认证客户端常见故障自查- 神州数码客户端

    神州数码客户端常见故障自查 一 客户端认证成功前故障 1 接上网线后网卡灯不亮 确定自己电脑网卡带灯 注 测试期间最好是不要接交换机 直接接墙上端口 参考方案 A 更换网线 B 如确认是端口故障 则请致电网络中心报修等待人过来维修 2 如果
  • (每日一练)MATLAB生成斐波那契数和数列

    今天 我学习的内容是利用MATLAB生成斐波那契数 先来介绍一下 斐波那契数列最初是用来解决兔子问题的 问题如下 一个人把一对兔子放在一个四面被墙包围的地方 假设每对兔子每个月都生一对新兔子 不 考虑伦理问题 那么一年可以从这对兔子中生产多
  • C/C++中__builtin_popcount()的使用及原理

    这个函数功能 返回输入数据中 二进制中 1 的个数 对于不同的使用类型 可以采用采用以下函数 builtin popcount int builtin popcountl long int builtin popcountll long l
  • Python列表推导式

    列表推导式 列表推导式使用非常简洁的方式来快速生成满足特定需求的列表 代码具有非常强的可读性 语法形式为 expression for expr1 in sequence1 if condition1 for expr2 in sequen
  • nginx之头部变量x_forwarded_for

    proxy add x forwarded for变量包含客户端请求头中的 X Forwarded For 与 remote addr两部分 他们之间用逗号分开 举个例子 有一个web应用 在它之前通过了2个nginx转发 即用户访问该we
  • P1518 [USACO2.4]两只塔姆沃斯牛 The Tamworth Two

    题目描述 两只牛逃跑到了森林里 Farmer John 开始用他的专家技术追捕这两头牛 你的任务是模拟他们的行为 牛和 John 追击在10 10 的平面网格内进行 一个格子可以是 一个障碍物 两头牛 它们总在一起 或者 Farmer Jo
  • 如何有效使用渲染农场?防止渲染出错的7个方法!

    如何使用渲染农场 又如何有效地使用渲染农场 使用云渲染农场时出错怎么办 众所周知我们可以在任意的笔记本或者终端PC上面来创作 3ds Max 场景 但是实际渲染是这样吗 其实不然 这其中的差距不是一星半点 只能说很可能会有两种不同的呈现 而
  • HTML+CSS+JS列表式视频播放页面

    HTML CSS JS列表式视频播放页面 无插件 应该没有 效果图 html
  • 31 Qt 之绘图之绘制一个漂亮的圆及圆弧

    一 圆形 经常地 我们会在网上看到一些列的抽奖活动 里面就有圆盘抽奖 是不是有点手痒了O O 效果 void MainWindow paintEvent QPaintEvent QPainter painter this painter s
  • 三英战SQL:解析NoSQL的可靠性及扩展操作

    摘要 NoSQL的高性能 易扩展及可靠性一直深受数据工作者的喜爱 然而对比传统关系型数据库NoSQL的优势究竟又在何处 Esen Sagynov在DZone上发布了一篇文章 从运行方面分析Cassandra HBase以及MongoDB产品
  • SVN 执行清理命令,提示清理以下路径失败,并可能有乱码的解决方法

    SVN 执行清理命令 提示清理以下路径失败 并可能有乱码的解决方法 参考文章 1 SVN 执行清理命令 提示清理以下路径失败 并可能有乱码的解决方法 2 https www cnblogs com aidegongyang p 830374
  • Windows下MySQL安装配置及libmysql的使用

    Windows下MySQL安装配置及libmysql的使用 1 安装配置 请参考 Mysql安装配置 2 API文档 libmysql API文档地址 3 示例代码 本示例代码使用了mysql stmt xxx接口 运行本代码需要提前创建好
  • Python之科赫曲线绘制

    实验四 py import turtle def koch size n if n 0 turtle fd size else for angle in 0 60 120 60 turtle left angle koch size 3 n
  • graphpad做图如何加星号_如何用R画分组柱状图并且添加标准差和显著性标记(星号)?...

    时间过了这么久 该交一份答案了 ggplot2包 是一个图形可视化包 并不带统计分析功能 所以统计学分析需要另外去做 这里加bar和显著性标识 如果了解ggplot2绘图原理中的图层概念的话 就能明白 无非就是在画完分组柱状图后 根据需要自
  • PCL 计算点云中任意一个点的法向量

    目录 一 算法原理 1 原理概述 2 主要函数 二 代码实现 三 结果展示 一 算法原理 1 原理概述 见 PCL 计算点云法向量并显示 2 主要函数 inline bool computePointNormal const pcl Poi
  • 自定义user表签发token、自定义认证类、simpleui模块使用

    Python微信订餐小程序课程视频 https edu csdn net course detail 36074 Python实战量化交易理财系统 https edu csdn net course detail 35475 今日内容概要
  • mysql的数据备份脚本

    一 数据库的备份脚本脚本命令 1 核心命令 usr local mysql bin mysqldump h IP p3306 u dbuser p dbpasswd f A F E R quick single transaction fl
  • Linux命令 - ls命令

    Linux命令 ls命令 ls命令是linux下最常用的命令 ls命令就是list的缩写 用来打印出当前目录的清单 如果ls指定其他目录 那么就会显示指定目录里的文件及文件夹清单 通过ls 命令不仅可以查看linux文件夹包含的文件 而且可
  • python多线程:Thread类的用法

    我们要创建Thread对象 然后让他们运行 每个Thread对象代表一个线程 在每个线程中我们可以让程序处理不同的任务 这就是多线程编程 创建Thread对象有两种方法 1 直接创建Thread 将一个callable对象从类的构造器传递出