Python 基于线程的并行 threading模块的用法

 更新时间:2025年06月12日 11:13:07   作者:V1ncent-CC  
threading模块是Python的高级线程接口,提供线程对象和同步工具,本文主要介绍了Python 基于线程的并行 threading模块的用法,感兴趣的可以了解一下

threading模块是基于_thread模块的高级线程接口,相比于低层的_thread模块,它提供了代表线程的对象和同步化工具,在threading模块中,只要任何一个派生线程(守护线程除外)在运行中,程序都不会退出,不再需要像_thread那样控制主线程等待。

一、threading模块基本用法

threading模块基于_thread进行了类封装,其用于构造线程的类为threading.Thread,通过创建类实例的方式来创建线程对象。

1.1 threading.Thread类

threading.Thread类的主要参数如下:

class threading.Thread(target=None, name=None, args=(), kwargs={}, *, daemon=None)

参数解释:

  • target,用于定义子线程的活动,这应该是个可调用的函数。
  • name,线程的名称。
  • args,可调用函数的参数。
  • kwargs,可调用函数的关键字参数。
  • daemon,是否将线程设置为守护线程。

通过调用threading.Thread类创建出来的实例即是线程对象,线程对象创建后,需要调用.start方法启动,这个方法会在独立的线程中唤起run方法,因此有2种方法可以定义子线程的活动:

  • 向构造类threading.Thread传递一个可调用对象(target参数)。
  • 通过子类重载run方法。

下面通过案例演示2种用法。

1.2 用法1:通过threading.Thread定义子线程活动

下面代码先定义一个函数func,随后调用threading.Thread创建2个线程对象,调用时通过target=func传递自定义函数(线程活动),最后通过线程对象的.start方法启动线程:

import threading

def func(id):
    print('我是 {} 号子线程'.format(id))

t1 = threading.Thread(target=func, args=(1,))
t2 = threading.Thread(target=func, args=(2,))

t1.start()
t2.start()

在这里插入图片描述

1.3 用法2:通过子类重载run方法定义子线程活动

还是上面的例子,这次通过重载子类的run方法来定义子线程活动,当调用线程对象的.start方法启动子线程时,子线程会自动调用.run方法。

import threading

def func(id):
    print('我是 {} 号子线程'.format(id))

# 创建子类MyThread, threading.Thread为其超类
class MyThread(threading.Thread):
    def __init__(self, id):
        self.id = id
        threading.Thread.__init__(self)
    
    # 重载run方法
    def run(self):
        print('我是 {} 号子线程'.format(self.id))

t1 = MyThread(1)
t2 = MyThread(2)

# 启动子线程,会调用子类中的run方法
t1.start()
t2.start()

在这里插入图片描述

二、threading模块其他方法

threading模块相对_thread模块额外提供了一些线程状态查询和控制工具,下面介绍几个主要方法。

2.1 共享访问控制 - threading.Lock

多线程必然会涉及到共享资源的访问,threading模块提供了一个.Lock方法(和_thread.allocate_lock是同一个东西)用于创建锁对象,通过锁来控制共享资源的访问,基于上面的类MyThread同时创建10个子线程:

for i in range(10):
    MyThread(i).start()

在这里插入图片描述

可以看到上面的输出出现了重叠,这是因为这10个子线程共享一个标准输出流,可能出现同时打印的情况。下面把MyThread类改造一下,通过.Lock对象来同步子线程的打印操作(每次打印前先获取锁,防止出现2个子线程同时打印):

class MyThread(threading.Thread):
    # 新增一个参数mutex,用于传入锁
    def __init__(self, id, mutex):
        self.id = id
        self.mutex = mutex
        threading.Thread.__init__(self)
    
    # 重载run方法
    def run(self):
        # 通过with语句管理锁,打印前先获取锁,打印完成释放
        with self.mutex:
            print('我是 {} 号子线程'.format(self.id))

# 创建一个锁对象
mutex = threading.Lock()

for i in range(10):
    # 每个子线程启动时都传入该锁
    MyThread(i, mutex).start()

在这里插入图片描述

  • mutex = threading.Lock() 创建了一个锁对象,并在创建子线程时传入(这10个子线程传入的是同一把锁)。
  • 子类的run方法,在执行print语句前新增了with self.mutex,这会尝试获取锁,如果当前锁被其他子线程占用(正在打印),那么它会等待。
  • 锁对象也可以通过.acuqure和.release方法手动获取和释放,但建议还是使用with来管理(方便)。

2.2 阻塞方法 - threading.Thread.join

每个线程对象都有一个join方法,这个方法会阻塞调用者,直到线程终结,这对于主线程来说是一个信号,通过join方法可以用来监控子线程的完成情况,适合执行在子线程完成后再执行的动作。

下面改造一下子类的run方法,让每个线程打印前随机等待1-5秒,然后调用Thread.join方法,这个方法会阻塞主线程(等待子线程完成),当所有子线程执行完毕时(.join方法返回),立刻打印"所有子线程执行完毕"。

下面代码在run方法的print之前加入了time.sleep(random.randint(1,5))让子线程随机睡眠1-5秒,因此子线程的结束时间是不确定的:

import time, random
class MyThread(threading.Thread):
    # 新增一个参数mutex,用于传入锁
    def __init__(self, id):
        self.id = id
        threading.Thread.__init__(self)
    
    # 重载run方法
    def run(self):
            # 打印之前随机睡眠1-5秒
            time.sleep(random.randint(1,5))
            print('我是 {} 号子线程'.format(self.id))

threads = []

for i in range(10):
    # 每个子线程启动时都传入该锁
    ti = MyThread(i)
    threads.append(ti)
    ti.start()

print("开始等待子线程执行...")    
for thread in threads:
    # 针对每个子线程都会调用.join方法,所有子线程结束后,才会继续向下执行
    thread.join()
print("所有子线程执行完毕...")    

在这里插入图片描述

可以看到通过Thread.join方法阻塞主线程,可以保证在所有子线程都执行完毕后,才打印"所有子线程执行完毕…"。

以上即是threading模块的基本用法,子线程还有.is_alive方法用来检测是否存活,另外还有递归锁可以用来嵌套获取锁,有兴趣的同学可以深入研究一下。

最后:由于Cpython的中存在"全局解释器锁(GIL)",这个会限制利用多核CPU的计算资源(Python4.0可能会修复),因此多线程比较适合I/O密集型任务,对于CPU密集型任务,推荐还是使用多进程并发(multiprocessing模块)。

到此这篇关于Python 基于线程的并行 threading模块的用法的文章就介绍到这了,更多相关Python 线程并行threading用法内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 通过python爬虫赚钱的方法

    通过python爬虫赚钱的方法

    在本篇文章里小编给大家分享了关于通过python爬虫赚钱的方法,有兴趣的朋友们学习下。
    2019-01-01
  • jupyter的安装与使用以及运行卡顿问题及解决

    jupyter的安装与使用以及运行卡顿问题及解决

    这篇文章主要介绍了jupyter的安装与使用以及运行卡顿问题及解决,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-06-06
  • Python OpenCV 调用摄像头并截图保存功能的实现代码

    Python OpenCV 调用摄像头并截图保存功能的实现代码

    这篇文章主要介绍了Python OpenCV 调用摄像头并截图保存功能,本文通过两段实例代码给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-07-07
  • python调用动态链接库的基本过程详解

    python调用动态链接库的基本过程详解

    这篇文章主要介绍了python调用动态链接库的基本过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-06-06
  • Flask框架各种常见装饰器示例

    Flask框架各种常见装饰器示例

    这篇文章主要介绍了Flask框架各种常见装饰器,结合实例形式简单分析了flask框架各种常见装饰器的功能、用法及相关操作注意事项,需要的朋友可以参考下
    2018-07-07
  • Django 实现图片上传和下载功能

    Django 实现图片上传和下载功能

    这篇文章主要介绍了Django 如何实现图片上传和下载功能,帮助大家更好的理解和使用django框架,感兴趣的朋友可以了解下
    2020-12-12
  • Python大数据之使用lxml库解析html网页文件示例

    Python大数据之使用lxml库解析html网页文件示例

    这篇文章主要介绍了Python大数据之使用lxml库解析html网页文件,结合实例形式分析了Python大数据操作中使用lxml库解析html网页具体步骤及相关注意事项,需要的朋友可以参考下
    2019-11-11
  • 使用Numpy打乱数组或打乱矩阵行

    使用Numpy打乱数组或打乱矩阵行

    这篇文章主要介绍了使用Numpy打乱数组或打乱矩阵行问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2023-05-05
  • Matlab中关于argmax、argmin函数的使用解读

    Matlab中关于argmax、argmin函数的使用解读

    这篇文章主要介绍了Matlab中关于argmax、argmin函数的使用解读,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • Python中集合的创建及常用函数的使用详解

    Python中集合的创建及常用函数的使用详解

    这篇文章主要为大家详细介绍了Python中集合的创建、使用和遍历,集合常见的操作函数,集合与列表,元组,字典的嵌套,感兴趣的小伙伴可以了解一下
    2022-06-06

最新评论