python——Thread类详解

这篇具有很好参考价值的文章主要介绍了python——Thread类详解。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

threading

threading库是python的线程模型,利用threading库我们可以轻松实现多线程任务。

threading模块包含的类

包含常用的Thread,Queue,Lock,Event,Timer等类
python thread,python专栏,python

threading模块常用方法

current_thread()

  • threading.current_thread() : 返回当前的Thread类对象(线程对象)
    在哪个线程中调用threading的current_thread方法就返回哪个线程。
import threading
# 在主线程中直接打印,可以看到返回主线程MainThread
print(threading.current_thread())  # <_MainThread(MainThread, started 22660)>
print(threading.current_thread().name)  # MainThread

import threading
import time

# 创建子线程,在子线程中打印,打印的是正在执行的子线程
def task():
    print('》》》线程:{thread}开始执行task'.format(thread=threading.current_thread().name))
    time.sleep(1)
    print('》》》线程:{thread}完成执行task'.format(thread=threading.current_thread().name))


t = threading.Thread(target=task, name='线程1')
t.start()

结果:

》》》线程:线程1开始执行task
》》》线程:线程1完成执行task

main_thread()

  • threading.main_thread() : 返回当前主线程对象。即:启动python解释器的线程对象
import threading
import time


def task():
    print('》》》线程:{thread}——开始执行task'.format(thread=threading.current_thread().name))
    time.sleep(1)
    print('》》》当前主线程:{thread}'.format(thread=threading.main_thread().name))
    print('》》》线程:{thread}——完成执行task'.format(thread=threading.current_thread().name))


t = threading.Thread(target=task, name='线程1')
t.start()

结果

》》》线程:线程1——开始执行task
》》》当前主线程:MainThread
》》》线程:线程1——完成执行task

get_ident()

  • threading.get_ident(): 返回当前线程的线程标识符。线程标识符是一个非负整数,并无特殊函数,只是用来标识线程,该整数可能会被循环利用。python3.3及以后版本支持该方法
import threading
import time


def task():
    print('》》》线程号:{}——开始执行task'.format(threading.get_ident()))
    time.sleep(5)


t1 = threading.Thread(target=task, name='线程1')
t1.start()

结果:

》》》线程号:35040——开始执行task

enumerate()

  • threading.enumerate(): 返回当前处于alive状态的所有Thread对象的list
    -alive状态指线程启动后、结束前,不包括启动前和终止后的线程。
import threading
import time


def task():
    print('》》》线程:{}——开始执行task'.format(threading.current_thread().name))
    time.sleep(5)
    print('》》》线程:{}——完成执行task'.format(threading.current_thread().name))


t1 = threading.Thread(target=task, name='线程1')
t1.start()
t2 = threading.Thread(target=task, name='线程2')
t2.start()
t3 = threading.Thread(target=task, name='线程3')
t3.start()
print(threading.enumerate())

结果:

[<_MainThread(MainThread, started 25232)>, <Thread(线程1, started 32200)>, <Thread(线程2, started 13064)>, <Thread(线程3, started 10452)>]

active_count()

  • threading.active_count() : 返回当前处于alive状态的线程个数
import threading
import time


def task():
    print('》》》线程:{thread}——开始执行task'.format(thread=threading.current_thread().name))
    time.sleep(5)
    print('》》》线程:{thread}——完成执行task'.format(thread=threading.current_thread().name))


t1 = threading.Thread(target=task, name='线程1')
t1.start()
t2 = threading.Thread(target=task, name='线程2')
t2.start()
t3 = threading.Thread(target=task, name='线程3')
t3.start()
time.sleep(0.1)
print('》》》当前活跃线程数:{}'.format(threading.active_count()))

结果:
可以看到活跃线程数为4,包括:主线程,线程1,线程2,线程3

》》》线程:线程1——开始执行task
》》》线程:线程2——开始执行task
》》》线程:线程3——开始执行task
》》》当前活跃线程数:4
》》》线程:线程3——完成执行task
》》》线程:线程1——完成执行task
》》》线程:线程2——完成执行task

stack_size

  • threading.stack_size([size]): 返回创建线程时使用的栈的大小。如果指定size参数,则用来指定后续创建的线程使用的栈大小,size必须是0(0表示使用系统默认值)或大于32K的正整数。
import threading

# 输出当前线程栈的大小 
print(threading.stack_size())  # 0
# 设置后续线程栈的大小
threading.stack_size(43*1024)  # 43*1024=44032
print(threading.stack_size())  # 44032

Thread类

Thread类是threading标准库中最重要的一个类。
你可以通过实例化Thread类,创建Thread对象,来创建线程。
你也可以通过继承Thread类创建派生类,并重写__init__和run方法,实现自定义线程对象类。

创建线程

1.实例化Thread类

通过实例化Thread类,来创建线程,通过target传入函数,即线程需要做的任务。
Thread类的构造函数:

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

target:传入目标函数的名称
name:为线程名,一般命名格式为:Thread-N (N为small decimal number)
args:传入目标函数的参数,格式为元组。若目标函数需传关键字参数,可直接传入构造函数关键字组成的字典,无需传到任何参数。
daemon:标明此线程是否为守护线程(后文介绍),默认为False。

import threading
import time


def task(seconds):
    print('》》》线程:{},开始执行task'.format(threading.current_thread().name))
    time.sleep(seconds)


t1 = threading.Thread(target=task, args=(5,), name='Thread-1')
print(t1)  # <Thread(Thread-1, initial)>

2.继承Thread类

创建子类继承Thread,通常重写run方法,在run方法写线程需要做的任务,然后实例化你创建的子类。
此方式具体原因请看下文的run方法。

启动线程

start()方法

调用Thread实例的start方法,会启动线程的活动。 每个线程对象最多只能调用一次。多次调用将引发RuntimeError错误。
线程启动后,将在子线程中,执行线程target传入的函数

import threading
import time


def task(seconds):
    print('》》》线程:{},开始执行task'.format(threading.current_thread().name))
    time.sleep(seconds)
    print('》》》线程:{},结束执行task'.format(threading.current_thread().name))


t1 = threading.Thread(target=task, args=(1,), name='Thread-1')
t1.start()  # 启动线程后,将在子线程中执行task函数

结果:

》》》线程:Thread-1,开始执行task
》》》线程:Thread-1,结束执行task

run()方法

用来实现线程的功能与业务逻辑,可以在子类中重写该方法来自定义线程的行为。
start()方法启动线程,实际上也是调用了run方法,在run方法中又调用了target传入的函数。
所以根本上,线程的功能和业务逻辑是在run方法去实现的,我们通过实例化Thread类target传入的功能函数也只是在run方法中去调了这个函数。
如果run方法没有被重写,并且target被定义,则会按照父类Thread的逻辑,直接调用实例化线程时传入target的方法,否则什么也不做。
python thread,python专栏,python

如果你不想直接实例化Thread类,通过target传入线程的功能和业务逻辑,
你可以创建Thread的子类,且此子类的run方法被重写,启动此子类实例化的线程,则会直接调用重写的run方法的内容,target传入的函数则不会再被调用,除非你重写的run方法中人为调用了target传入的函数。所以在这种情况下,也不用再单独写个函数传入target。

import threading
import time


def task(seconds):
    print('》》》线程:{},开始执行task'.format(threading.current_thread().name))
    time.sleep(seconds)
    print('》》》线程:{},结束执行task'.format(threading.current_thread().name))


class MyThread(threading.Thread):
    def run(self) -> None:
        print('run_test')

# Mythread类重写了run方法,则线程调用重写的run方法,target传入的函数将不起作用
t1 = MyThread(target=task, args=(1,), name='Thread-1')
t1.start()

结果:

run_test

综上,可以得出,如果你想创建线程:
1.可以直接通过创建实例化Thread类创建,并且通过target传入你想做的事情。
2.继承Thread类,重写run方法,在run方法中写你想做的事情,然后实例化你创建的子类。

线程执行顺序

在默认情况下,线程执行:

import threading
import time


def task(seconds):
    print('》》》线程:{},开始执行task'.format(threading.current_thread().name))
    while seconds > 0:
        print('》》》线程:{0},倒计时:{1}秒'.format(threading.current_thread().name, seconds))
        time.sleep(1)
        seconds -= 1

    print('》》》线程:{},结束执行task'.format(threading.current_thread().name))


t1 = threading.Thread(target=task, args=(2,), name='Thread-1')
t2 = threading.Thread(target=task, args=(2,), name='Thread-2')

t1.start()
t2.start()

print('》》》主线程打印:主线程的print')
print('》》》主线程打印:当前存活线程:{}'.format(threading.enumerate()))

结果:

》》》线程:Thread-1,开始执行task
》》》线程:Thread-1,倒计时:2秒
》》》线程:Thread-2,开始执行task
》》》主线程打印:主线程的print
》》》线程:Thread-2,倒计时:2秒
》》》主线程打印:当前存活线程:[<_MainThread(MainThread, started 19876)>, <Thread(Thread-1, started 24152)>, <Thread(Thread-2, started 35436)>]
》》》线程:Thread-1,倒计时:1秒
》》》线程:Thread-2,倒计时:1秒
》》》线程:Thread-1,结束执行task
》》》线程:Thread-2,结束执行task

由结果可以看出:
主线程代码顺序执行创建t1,t2两个Thread实例
子线程t1开始执行,并执行倒计时,遇到time的sleep阻塞时,释放GIL锁
此时,子线程t2开始执行
主线程的执行不受t1和t2两个子线程的影响,代码走到这,直接正常执行主线程打印
子线程t2执行倒计时,遇到time的sleep阻塞时,释放GIL锁,
主线程继续正常执行,打印当前存活线程。
子线程t2释放GIL锁之后,被子线程t1拿到,两者交替完成任务。

由以上可以看出:
1.主线程代码顺序执行,不会受子线程阻塞的影响。
2.主线程代码执行完毕后,会等待所有子线程执行完毕。
3.子线程遇到阻塞,会释放GIL锁,其他子线程继续执行,交替完成。

如果你想让子线程启动后,主线程代码阻塞,那么你可以使用子线程的join方法。
如果你想让主线程执行完毕后,不再等待未完成的子线程,可以将子线程设置为守护线程。

join()方法

Thread实例的join(timeout=None)方法用于阻塞主线程,可以想象成将某个子线程的执行过程插入(join)到主线程的时间线上,主线程的后续代码延后执行。
通俗地说就是是子线程告诉主线程,你要在设置join的位置等我执行完毕你再往下执行。

  • 一个线程中调用另一个线程的join方法,调用者将被阻塞,直到被调用线程终止(正常退出或者抛出未处理的异常或者到达timeout的等待时间)。
  • timeout参数指定调用者等待多久,如果没有设置超时,就一直等到被调用线程结束。
  • 一个线程可以被join多次。
  • join一定要在线程启动以后调用。

在上文线程执行顺序代码中,加入线程调用join方法:

import threading
import time


def task(seconds):
    print('》》》线程:{},开始执行task'.format(threading.current_thread().name))
    while seconds > 0:
        print('》》》线程:{0},倒计时:{1}秒'.format(threading.current_thread().name, seconds))
        time.sleep(1)
        seconds -= 1

    print('》》》线程:{},结束执行task'.format(threading.current_thread().name))


t1 = threading.Thread(target=task, args=(2,), name='Thread-1')
t2 = threading.Thread(target=task, args=(2,), name='Thread-2')

t1.start()
t2.start()

t1.join()  # 主线程调用t1线程的join方法,那么主线程(调用者)将被阻塞,直到t1(被调用者)执行结束。
t2.join()

print('》》》主线程打印:主线程的print')
print('》》》主线程打印:当前存活线程:{}'.format(threading.enumerate()))

结果:

》》》线程:Thread-1,开始执行task
》》》线程:Thread-1,倒计时:2秒
》》》线程:Thread-2,开始执行task
》》》线程:Thread-2,倒计时:2秒
》》》线程:Thread-1,倒计时:1秒
》》》线程:Thread-2,倒计时:1秒
》》》线程:Thread-2,结束执行task
》》》线程:Thread-1,结束执行task
》》》主线程打印:主线程的print
》》》主线程打印:当前存活线程:[<_MainThread(MainThread, started 37320)>]

可以发现,主线程被阻塞掉,t1,t2线程执行完毕后,主线程打印函数才执行

daemon线程(守护线程)

上文提到,主线程代码执行完毕后,会等待所有子线程执行完毕,整个程序才退出。
因为子线程默认为【非守护线程】,主线程代码执行完毕,各子线程会继续运行,直到所有【非守护线程】结束,python程序退出。

如何理解守护线程:
是为守护别人而存在,当设置为守护线程后,被守护的主线程不存在后,守护线程也自然不存在,直接结束。
所以说,守护线程是不被考虑,爱允许到哪运行到哪,主线程不关心,不等待。可以理解为随时可以被终止的线程。

试用场景:
后台任务。如:发送心跳包,监控,这种场景最多
主线程工作时,才有用的线程。如:主线程中维护公共的资源,主线程已经清理了,准备退出,而工作线程使用这些资源工作也没有意义了,一起退出最合适。

注意
如果daemon线程使用了join方法,主线程仍然会被阻塞,等待此线程执行完毕后主线程才会退出,所以这个线程的守护就没有意义了,它仍然是主线程需要等待线程。

所以如果你想让主线程执行完毕后,不再等待未完成的子线程,可以将子线程设置为守护线程(避开使用join方法):

daemon=True
  • 实例化Thread时,传入daemon为True,标明此线程为守护线程
    t1 = threading.Thread(target=task, args=(2,), name='Thread-1', daemon=True)
setDaemon(daemonic)方法

调用线程实例的setDaemon(daemonic)方法,daemonic传入True或者False。可以在线程创建以后再设置线程为守护线程。

  • 务必在线程start前进行设置,否则会报异常。
t2 = threading.Thread(target=task, args=(2,), name='Thread-2')
t2.setDaemon(True)

直接对其daemon属性进行复制也行

t2 = threading.Thread(target=task, args=(2,), name='Thread-2')
t2.daemon = True

让我们来看一下守护线程的效果:

import threading
import time


def task(seconds):
    print('》》》线程:{},开始执行task'.format(threading.current_thread().name))
    while seconds > 0:
        print('》》》线程:{0},倒计时:{1}秒'.format(threading.current_thread().name, seconds))
        time.sleep(1)
        seconds -= 1

    print('》》》线程:{},结束执行task'.format(threading.current_thread().name))


t1 = threading.Thread(target=task, args=(2,), name='Thread-1', daemon=True)
t2 = threading.Thread(target=task, args=(2,), name='Thread-2')
t2.setDaemon(True)
t1.start()
t2.start()

print('》》》主线程打印:主线程的print')
print('》》》主线程打印:当前存活线程:{}'.format(threading.enumerate()))

结果:

》》》线程:Thread-1,开始执行task
》》》线程:Thread-1,倒计时:2秒
》》》线程:Thread-2,开始执行task
》》》主线程打印:主线程的print
》》》线程:Thread-2,倒计时:2秒》》》主线程打印:当前存活线程:[<_MainThread(MainThread, started 33608)>, <Thread(Thread-1, started daemon 36944)>, <Thread(Thread-2, started daemon 36532)>]

从以上结果可以看出,主线程打印完毕后,程序直接退出,因为t1和t2线程都是守护线程,都不被主线程关心,不被主线程等待。

Thread实例的其他属性和方法

属性 含义
name 只是一个名字,只是个标识,可以重复
ident 线程ID,它是非0整数,线程启动后才会有ID,否则为None,线程退出后此ID仍旧可以访问,此ID可以重复使用
daemon 是否为daemon线程的布尔值
方法 含义
is_alive() 返回线程是否活着
isDaemon() 返回线程是否为守护线程
getName() 返回线程名。
setName() 设置线程名。

多个线程创建使用

在需要创建多个线程时,可以将线程放入列表中,start,join方法都可以试用for循环进行调用。
第一个for循环同时启动了所有子线程,随后在第二个for循环中执行t.join() ,主线程实际被阻塞的总时长等于其中执行时间最长的一个子线程。
一定注意:所以线程必须全部start完毕,才可以进行join,所以start和join的for循环要分开写。

import threading
import time


def task(seconds):
    print('》》》线程:{},开始执行task'.format(threading.current_thread().name))
    time.sleep(1)
    # while seconds > 0:
    #     print('》》》线程:{0},倒计时:{1}秒'.format(threading.current_thread().name, seconds))
    #     time.sleep(1)
    #     seconds -= 1

    print('》》》线程:{},结束执行task'.format(threading.current_thread().name))


thread_list = []
for i in range(5):
    t = threading.Thread(target=task, name="Thread-" + str(i + 1), args=(1,))
    thread_list.append(t)

for thread in thread_list:
    thread.start()

for thread in thread_list:
    thread.join()

print('》》》主线程打印:主线程的print')
print('》》》主线程打印:当前存活线程:{}'.format(threading.enumerate()))

结果:

》》》线程:Thread-1,开始执行task
》》》线程:Thread-2,开始执行task
》》》线程:Thread-3,开始执行task
》》》线程:Thread-2,结束执行task
》》》线程:Thread-1,结束执行task
》》》线程:Thread-3,结束执行task
》》》主线程打印:主线程的print
》》》主线程打印:当前存活线程:[<_MainThread(MainThread, started 31036)>]

Process finished with exit code 0

Timer类

threading.Timer继承自Thread,所以就是线程类,具有线程的能力和特征,他的实例能够延时执行目标函数的线程,相当于一个定时器。

并且在到达既定时间真正执行目标函数之前,你都可以cancel,撤销执行。

实例创建:

threading.timer(interval, function, args=None, kwargs=None)

start方法执行以后,Timer对象处于等待状态,既定时间到后,开始执行function函数

import threading


def add(x, y):
    print(x, y)

# 3秒钟之后执行线程
t = threading.Timer(3, add, args=(4, 5))
t.start()
print(threading.enumerate())  # [<_MainThread(MainThread, started 4301424000)>, <Timer(Thread-1, started 6184873984)>]

t.cancel()

只要这个Timer还没正式启动,还在等待,就可以使用cancel()方法停止掉

如果时间已经到了,函数已经开始执行了,那么这个cancel就没有任何效果了

import threading
import time


def add(x, y):
    print(x, y)


t = threading.Timer(3, add, args=(4, 5))
t.start()

time.sleep(2)
t.cancel()
print('主线程打印')

以上代码在线程未到执行时间前,执行了cancel方法,进行了撤回,所以线程不会再启动。文章来源地址https://www.toymoban.com/news/detail-771481.html

到了这里,关于python——Thread类详解的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【C++】多线程(thread)使用详解

    多线程(multithreading),是指在软件或者硬件上实现多个线程并发执行的技术。具有多核CPU的支持的计算机能够真正在同一时间执行多个程序片段,进而提升程序的处理性能。在一个程序中,这些独立运行的程序片段被称为“ 线程 ”(Thread),利用其编程的概念就叫作“多线

    2024年02月14日
    浏览(41)
  • 多线程系列(二) -Thread类使用详解

    在之前的文章中,我们简单的介绍了线程诞生的意义和基本概念,采用多线程的编程方式,能充分利用 CPU 资源,显著的提升程序的执行效率。 其中 java.lang.Thread 是 Java 实现多线程编程最核心的类,学习 Thread 类中的方法,是学习多线程的第一步。 下面我们就一起来看看,创

    2024年02月19日
    浏览(44)
  • Java 线程池(Thread Pools)详解

    目录 1、线程池介绍 2、线程池执行原理 3、线程池中的阻塞队列 4、Java 线程池中的拒绝策略 5、Java 提供的创建线程池的方式 6、线程池的使用示例 7、ForkJoinPool 和 ThreadPool 的区别 1、线程池介绍          线程池是一种重用线程的机制 ,用于提高线程的利用率和管理线程的

    2024年02月05日
    浏览(40)
  • 【.NET Core】 多线程之(Thread)详解

    线程 被定义为程序的执行路径。每个线程都定义了一个独特的控制流。如果您的应用程序涉及复杂且耗时的操作,那么设置不同的线程执行路径往往事半功倍,让每个线程执行特定的工作任务。 线程是一个 轻量级进程 。一个使用线程的常见实例是操作系统中并行编程的实现

    2024年01月20日
    浏览(39)
  • Python线程(thread)

    Python实用教程_spiritx的博客-CSDN博客 在Python3中,通过threading模块提供线程的功能。原来的thread模块已废弃。但是threading模块中有个Thread类(大写的T,类名),是模块中最主要的线程类,一定要分清楚了,千万不要搞混了。 threading模块提供了一些比较实用的方法或者属性,例

    2024年02月09日
    浏览(49)
  • Python多线程之_thread与threading模块

    Python多线程之_thread与threading模块 在Python程序中,多线程的应用程序会创建一个函数,来执行需要重复执行多次的程序代码,然后创建一个线程执行该函数。一个线程是一个应用程序单元,用于在后台并行执行多个耗时的动作。 在多线程的应用程序中,每一个线程的执行时间

    2024年02月04日
    浏览(37)
  • 【python | Thread】多线程记录

    every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?type=blog python 多线程记录 说明: 由于GIL锁的存,并非真正意义上的多线程 说明: 重写Thread类里面的run方法 ,里面写入你需要执行的代码,之后使用start()方法就可以调用run [1] https://zhuanlan.zhihu.com/p/91601

    2024年02月03日
    浏览(42)
  • 【Java系列】详解多线程(二)——Thread类及常见方法(下篇)

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习Java的一点学习心得,欢迎大家在评论区交流讨论💌 在操作系统中创建线程时,通常会同时创建相应的PCB并将其

    2024年02月04日
    浏览(38)
  • 【Java系列】详解多线程(二)——Thread类及常见方法(上篇)

    个人主页:兜里有颗棉花糖 欢迎 点赞👍 收藏✨ 留言✉ 加关注💓本文由 兜里有颗棉花糖 原创 收录于专栏【Java系列专栏】【JaveEE学习专栏】 本专栏旨在分享学习Java的一点学习心得,欢迎大家在评论区交流讨论💌 我们先来回顾一下线程与进程之间的联系。 我们知道多进程

    2024年02月04日
    浏览(46)
  • Jmeter系列-阶梯加压线程组Stepping Thread Group详解(6)

    tepping Thread Group是第一个自定义线程组但,随着版本的迭代,已经有更好的线程组代替Stepping Thread Group了【Concurrency Thread Group】,所以说Stepping Thread Group已经是过去式了,但还是介绍一下 有预览图显示估计的负载; 可延迟启动线程组; 可持续增加线程负载; 可设置最大负载

    2024年02月09日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包