Python多线程编程详解:创建、守护线程与常用方法

季宁
季宁
发布时间:2026-10-13 | 2人浏览过
原创

本文详细介绍Python中多线程的创建方式,包括函数式与类继承式两种方法。深入解析threading模块中Thread类的核心方法,如join()实现线程阻塞等待,setDaemon(True)设置守护线程以控制主进程退出行为,以及isAlive、activeCount等状态查询方法,帮助开发者掌握Python并发编程基础。

在Python中,创建线程主要存在两种途径:一是通过函数直接调用,二是通过定义类来实例化线程对象。

函数式创建是指直接调用_thread模块中的start_new_thread()函数来生成一个新的线程。

基于类的创建方式则是通过继承threading.Thread子类,从而包装并管理一个线程对象。

1.线程的创建

1.1 通过thread类直接创建

import threading
  import time
  def foo(n):
    time.sleep(n)
    print("foo func:",n)
  def bar(n):
    time.sleep(n)
    print("bar func:",n)
  s1=time.time()
  #创建一个线程实例t1,foo为这个线程要运行的函数
  t1=threading.Thread(target=foo,args=(3,))
  t1.start()  #启动线程t1
  #创建一个线程实例t2,bar为这个线程要运行的函数
  t2=threading.Thread(target=bar,args=(5,))
  t2.start()  #启动线程t2
  print("ending")
  s2=time.time()
  print("cost time:",s2-s1)

在示例程序中,定义了两个函数,它们会先执行几秒钟的休眠操作,随后打印一条提示信息。

接着,程序实例化两个线程对象,分别调用上述函数执行任务,最后统计并打印整个程序的总执行时间。

程序运行结果如下:

ending
cost time: 0.002000093460083008
foo func: 3
bar func: 5

执行时,父线程首先运行并打印“ending”,随后记录父线程的执行时间,最后才轮到子线程执行。

1.2 通过thread类来继承式创建

 import threading
  import time
  # 定义MyThread类,其继承自threading.Thread这个父类
  class MyThread(threading.Thread): 
    def __init__(self):
      threading.Thread.__init__(self)
    def run(self):
      print("ok")
      time.sleep(2)
      print("end t1")
  # 对类进行实例化
  t1=MyThread()  
  # 启动线程
  t1.start()
  print("ending")

2. Thread类的一些常用方法

2.1 join():在子线程完成之前,主线程将一直被阻塞

需要注意的是,线程的join方法必须在子线程的start方法被调用之后才能定义。 在第一个示例代码中加入两行关键代码,具体调整如下:

import threading
  import time
  def foo(n):
    time.sleep(n)
    print("foo func:",n)
  def bar(n):
    time.sleep(n)
    print("bar func:",n)
  s1=time.time()
  t1=threading.Thread(target=foo,args=(3,))
  t1.start()
  t2=threading.Thread(target=bar,args=(5,))
  t2.start()
  t1.join()    # 阻塞t1线程
  t2.join()    # 阻塞t2线程
  print("ending")
  s2=time.time()
  print("cost time:",s2-s1)

再次执行程序,观察到的运行结果如下:

foo func: 3
bar func: 5
ending
cost time: 5.002285957336426

当程序运行至子线程t1的foo方法时,会执行3秒的睡眠;与此同时,子线程t2也在进行睡眠操作。

待子线程t1的睡眠结束后,开始打印foo函数中的print语句,随后子线程t1执行完毕。

2秒钟之后,子线程t2的睡眠结束,开始打印bar函数中的print语句,随后子线程t2也执行完成。

在此期间,主线程一直处于阻塞状态,直到所有子线程执行完成后,主线程才会继续执行。

2.2 setDeamon(True)

setDaemon方法的作用是将线程声明为守护线程,该设置必须在调用start()方法之前完成。 若未设置为守护线程,程序可能会因等待子线程而无限挂起。

在程序执行过程中,当创建一个主线程并由此衍生出一个子线程时,主线程和子线程会各自独立运行。

当主线程运行结束时,它会检查子线程是否已执行完毕;如果子线程已完成,则主线程会等待子线程彻底退出后再结束自身。

然而,有时希望主线程执行完毕后,无论子线程是否完成,都立即随主线程一起退出,此时就需要调用setDaemon方法。

以第一个例子为例,现在希望让子线程t1和t2随主线程关闭,相关代码如下:

 import threading
  import time
  def foo(n):
    print("foo start")
    time.sleep(n)
    print("foo end...")
  def bar(n):
    print("bar start")
    time.sleep(n)
    print("bar end...")
  s1 = time.time()
  t1 = threading.Thread(target=foo, args=(3,))
  t1.setDaemon(True)
  t1.start()
  t2 = threading.Thread(target=bar, args=(5,))
  t2.setDaemon(True)
  t2.start()
  print("ending")
  s2 = time.time()
  print("cost time:", s2 - s1)

程序运行结果如下 :

foo start
bar start
ending
cost time: 0.003000020980834961

可以看到,将t1和t2都声明为守护线程后,程序自上而下执行:先执行子线程t1中的foo方法,打印第一条语句,随后子线程t1进入睡眠状态。

接着子线程t2执行,打印bar函数的第一条print语句,随后子线程t2进入睡眠状态,程序控制权切换回主线程。

主线程打印完“ending”语句后,检测到子线程t1和t2已被设置为守护线程,因此无需等待子线程执行完成,而是立即结束并打印整个程序的执行时间。

整个程序随即跟随主线程一起关闭。

2.3 子线程的一些其他方法

isAlive()      #判断一个线程是否是活动线程
getName()      #返回线程的名字
setName()      #设置线程的名字
  import threading
  import time
  def foo(n):
    time.sleep(n)
    print("foo func:", n)
  def bar(n):
    time.sleep(n)
    print("bar func:", n)
  s1 = time.time()
  t1 = threading.Thread(target=foo, args=(3,))
  t1.setDaemon(True)
  print("线程还未启动时,判断t1是否是活动的线程:", t1.isAlive()) # 线程还未启动,所以是False
  t1.start() # 启动线程
  print("线程已启动时,判断t1是否是活动的线程:", t1.isAlive()) # 线程已启动,所以是True
  print("修改前的线程名为:",t1.getName()) # 获取线程名
  t1.setName("t1")    #设置线程名
  print("修改后的线程名为:",t1.getName()) # 获取线程名
  t1.join()
  print("线程执行完成时,判断t1是不否是活动的线程:", t1.isAlive()) # 线程已执行完成,所以是False
  # print(threading.activeCount())
  print("ending")
  s2 = time.time()
  print("cost time:", s2 - s1)

程序执行结果:

线程还未启动时,判断t1是否是活动的线程: False 线程已启动时,判断t1是否是活动的线程: True 修改前的线程名为: Thread-1 修改后的线程名为: t1 foo func: 3 线程执行完成时,判断t1是不否是活动的线程: False ending cost time: 3.001171588897705

3.threading模块提供的一些方法

threading.currentThread()  #返回当前的线程变量
threading.enumerate()    #返回一个包含正在运行的线程的列表,不包括启动前和终止后的线程
threading.activeCount()   #返回正在运行的线程数量,等同于len(threading.enumerate())
  import threading
  import time
  def foo(n):
    time.sleep(n)
    print("foo func:", n)
  def bar(n):
    time.sleep(n)
    print("bar func:", n)
  s1 = time.time()
  t1 = threading.Thread(target=foo, args=(3,))
  t1.setDaemon(True)
  t1.start()
  t2 = threading.Thread(target=bar, args=(5,))
  t2.setDaemon(True)
  t2.start()
  print("程序中正在运行的线程数量:",threading.activeCount())
  print("程序中当前的线程变量:",threading.currentThread())
  print("当前正在运行的线程的列表:",threading.enumerate())
  print("ending")
  s2 = time.time()
  print("cost time:", s2 - s1)

程序执行结果:

程序中正在运行的线程数量: 3 程序中当前的线程变量: <_MainThread(MainThread, started 7064)> 当前正在运行的线程的列表: [<_MainThread(MainThread, started 7064)>, <Thread(Thread-1, started daemon 6384)>, <Thread(Thread-2, started daemon 2640)>] ending cost time: 0.002000093460083008

总结

以上所述是小编给大家介绍的详解python中的线程,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对本站网站的支持!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表