一文速通Python并行计算:02 Python多线程编程-threading模块、线程的创建和查询与守护线程

一文速通 Python 并行计算:02 Python 多线程编程-threading 模块、线程的创建和查询与守护线程

摘要:

本文介绍了 Python threading 模块的核心功能,包括线程创建与管理、线程状态监控以及守护线程的特殊应用,重点讲解了 Thread 类的实例化方法、获取当前线程信息、检测线程存活状态,以及如何实现后台线程。

关于我们更多介绍可以查看云文档: Freak 嵌入式工作室云文档,或者访问我们的 wiki:****https://github.com/leezisheng/Doc/wik

原文链接:

FreakStudio的博客

往期推荐:

学嵌入式的你,还不会面向对象??!

全网最适合入门的面向对象编程教程:00 面向对象设计方法导论

全网最适合入门的面向对象编程教程:01 面向对象编程的基本概念

全网最适合入门的面向对象编程教程:02 类和对象的 Python 实现-使用 Python 创建类

全网最适合入门的面向对象编程教程:03 类和对象的 Python 实现-为自定义类添加属性

全网最适合入门的面向对象编程教程:04 类和对象的Python实现-为自定义类添加方法

全网最适合入门的面向对象编程教程:05 类和对象的Python实现-PyCharm代码标签

全网最适合入门的面向对象编程教程:06 类和对象的Python实现-自定义类的数据封装

全网最适合入门的面向对象编程教程:07 类和对象的Python实现-类型注解

全网最适合入门的面向对象编程教程:08 类和对象的Python实现-@property装饰器

全网最适合入门的面向对象编程教程:09 类和对象的Python实现-类之间的关系

全网最适合入门的面向对象编程教程:10 类和对象的Python实现-类的继承和里氏替换原则

全网最适合入门的面向对象编程教程:11 类和对象的Python实现-子类调用父类方法

全网最适合入门的面向对象编程教程:12 类和对象的Python实现-Python使用logging模块输出程序运行日志

全网最适合入门的面向对象编程教程:13 类和对象的Python实现-可视化阅读代码神器Sourcetrail的安装使用

全网最适合入门的面向对象编程教程:全网最适合入门的面向对象编程教程:14 类和对象的Python实现-类的静态方法和类方法

全网最适合入门的面向对象编程教程:15 类和对象的 Python 实现-__slots__魔法方法

全网最适合入门的面向对象编程教程:16 类和对象的Python实现-多态、方法重写与开闭原则

全网最适合入门的面向对象编程教程:17 类和对象的Python实现-鸭子类型与"file-like object"

全网最适合入门的面向对象编程教程:18 类和对象的Python实现-多重继承与PyQtGraph串口数据绘制曲线图

全网最适合入门的面向对象编程教程:19 类和对象的 Python 实现-使用 PyCharm 自动生成文件注释和函数注释

全网最适合入门的面向对象编程教程:20 类和对象的Python实现-组合关系的实现与CSV文件保存

全网最适合入门的面向对象编程教程:21 类和对象的Python实现-多文件的组织:模块module和包package

全网最适合入门的面向对象编程教程:22 类和对象的Python实现-异常和语法错误

全网最适合入门的面向对象编程教程:23 类和对象的Python实现-抛出异常

全网最适合入门的面向对象编程教程:24 类和对象的Python实现-异常的捕获与处理

全网最适合入门的面向对象编程教程:25 类和对象的Python实现-Python判断输入数据类型

全网最适合入门的面向对象编程教程:26 类和对象的Python实现-上下文管理器和with语句

全网最适合入门的面向对象编程教程:27 类和对象的Python实现-Python中异常层级与自定义异常类的实现

全网最适合入门的面向对象编程教程:28 类和对象的Python实现-Python编程原则、哲学和规范大汇总

全网最适合入门的面向对象编程教程:29 类和对象的Python实现-断言与防御性编程和help函数的使用

全网最适合入门的面向对象编程教程:30 Python的内置数据类型-object根类

全网最适合入门的面向对象编程教程:31 Python的内置数据类型-对象Object和类型Type

全网最适合入门的面向对象编程教程:32 Python的内置数据类型-类Class和实例Instance

全网最适合入门的面向对象编程教程:33 Python的内置数据类型-对象Object和类型Type的关系

全网最适合入门的面向对象编程教程:34 Python的内置数据类型-Python常用复合数据类型:元组和命名元组

全网最适合入门的面向对象编程教程:35 Python的内置数据类型-文档字符串和__doc__属性

全网最适合入门的面向对象编程教程:36 Python的内置数据类型-字典

全网最适合入门的面向对象编程教程:37 Python常用复合数据类型-列表和列表推导式

全网最适合入门的面向对象编程教程:38 Python常用复合数据类型-使用列表实现堆栈、队列和双端队列

全网最适合入门的面向对象编程教程:39 Python常用复合数据类型-集合

全网最适合入门的面向对象编程教程:40 Python常用复合数据类型-枚举和enum模块的使用

全网最适合入门的面向对象编程教程:41 Python常用复合数据类型-队列(FIFO、LIFO、优先级队列、双端队列和环形队列)

全网最适合入门的面向对象编程教程:42 Python常用复合数据类型-collections容器数据类型

全网最适合入门的面向对象编程教程:43 Python常用复合数据类型-扩展内置数据类型

全网最适合入门的面向对象编程教程:44 Python内置函数与魔法方法-重写内置类型的魔法方法

全网最适合入门的面向对象编程教程:45 Python实现常见数据结构-链表、树、哈希表、图和堆

全网最适合入门的面向对象编程教程:46 Python函数方法与接口-函数与事件驱动框架

全网最适合入门的面向对象编程教程:47 Python函数方法与接口-回调函数Callback

全网最适合入门的面向对象编程教程:48 Python函数方法与接口-位置参数、默认参数、可变参数和关键字参数

全网最适合入门的面向对象编程教程:49 Python函数方法与接口-函数与方法的区别和lamda匿名函数

全网最适合入门的面向对象编程教程:50 Python函数方法与接口-接口和抽象基类

全网最适合入门的面向对象编程教程:51 Python函数方法与接口-使用Zope实现接口

全网最适合入门的面向对象编程教程:52 Python函数方法与接口-Protocol协议与接口

全网最适合入门的面向对象编程教程:53 Python字符串与序列化-字符串与字符编码

全网最适合入门的面向对象编程教程:54 Python字符串与序列化-字符串格式化与format方法

全网最适合入门的面向对象编程教程:55 Python字符串与序列化-字节序列类型和可变字节字符串

全网最适合入门的面向对象编程教程:56 Python字符串与序列化-正则表达式和re模块应用

全网最适合入门的面向对象编程教程:57 Python字符串与序列化-序列化与反序列化

全网最适合入门的面向对象编程教程:58 Python字符串与序列化-序列化Web对象的定义与实现

全网最适合入门的面向对象编程教程:59 Python并行与并发-并行与并发和线程与进程

一文速通Python并行计算:00 并行计算的基本概念

一文速通Python并行计算:01 Python多线程编程-基本概念、切换流程、GIL锁机制和生产者与消费者模型

更多精彩内容可看:

给你的 Python 加加速:一文速通 Python 并行计算

一文搞懂 CM3 单片机调试原理

肝了半个月,嵌入式技术栈大汇总出炉

电子计算机类比赛的"武林秘籍"

一个MicroPython的开源项目集锦:awesome-micropython,包含各个方面的Micropython工具库

Avnet ZUBoard 1CG开发板---深度学习新选择

工程师不要迷信开源代码,还要注重基本功

什么?配色个性化的电机驱动模块?!!

什么?XIAO主控新出三款扩展板!

手把手教你实现Arduino发布第三方库

万字长文手把手教你实现MicroPython/Python发布第三方库

文档获取:

可访问如下链接进行对文档下载:

https://github.com/leezisheng/Doc

该文档是一份关于 并行计算Python 并发编程 的学习指南,内容涵盖了并行计算的基本概念、Python 多线程编程、多进程编程以及协程编程的核心知识点:

正文

1.Python threading 模块

Python3 实现多线程编程需要借助于 threading 模块,threading 是 Python 标准库中的一个模块,它提供了一个高级的面向对象的线程编程接口。使用 threading 模块可以更方便地创建和管理线程,包括线程同步、线程通信、线程优先级等功能。(在 Python2 中,也有 thread 模块,它提供了一些基本的线程操作函数,例如 start_new_thread()函数用于创建新线程,exit()函数用于退出线程等。thread 模块只能在 Python 2 中使用。)

threading 模块包括以下组件:

  • **(1)Thread 线程类,**这是我们用的最多的一个类,你可以指定线程函数执行或者继承自它都可以实现子线程功能;
  • **(2)Timer 与 Thread 类似,**但要等待一段时间后才开始运行;
  • **(3)Lock 锁,**这个我们可以对全局变量互斥时使用;
  • **(4)RLock 可重入锁,**使单线程可以再次获得已经获得的锁;
  • **(5)Condition 条件变量,**能让一个线程停下来,等待其他线程满足某个"条件";
  • **(6)Event 通用的条件变量。**多个线程可以等待某个事件发生,在事件发生后,所有的线程都被激活;
  • 7)Semaphore 为等待锁的线程提供一个类似"等候室"的结构;
  • (8)BoundedSemaphore 与 semaphore 类似,但不允许超过初始值;
  • **(9)Queue:**实现了多生产者(Producer)、多消费者(Consumer)的队列,支持锁原语,能够在多个线程之间提供很好的同步支持。

2.线程创建

使用线程最简单的一个方法是,用一个目标函数实例化一个 Thread 然后调用 start() 方法启动它。Python 的 threading 模块提供了 Thread() 方法在不同的线程中运行函数或处理过程等。

Thread 类代表一个在独立控制线程中运行的活动。该类提供的函数包括:

函数名称 作用
getName(self) 返回线程的名字
isAlive(self) 布尔标志,表示这个线程是否还在运行中
isDaemon(self) 返回线程的 daemon 标志
join(self, timeout=None) 程序挂起,直到线程结束,如果给出 timeout,则最多阻塞 timeout 秒
run(self) 定义线程的功能函数
setDaemon(self, daemonic) 用于设置线程是否为守护线程
setName(self, name) 设置线程的名字
start(self) 开始线程执行

一般来说,新建线程有两种模式,一种是创建线程要执行的函数,把这个函数传递进 Thread 对象里,让它来执行;另一种是直接从 Thread 继承,创建一个新的 class,把线程执行的代码放到这个新的 class 里。

2.1 调用 Thread 类的构造器创建线程

Thread 类提供了如下的 __init__() 构造器,可以用来创建线程:

复制代码
__init__(self, group=None, target=None, name=None, args=(), kwargs=None, *,daemon=None)

此构造方法中,以上所有参数都是可选参数,即可以使用,也可以忽略。其中各个参数的含义如下:

  • group:指定所创建的线程隶属于哪个线程组;
  • target:当线程启动的时候要执行的函数;
  • name: 线程的名字,默认会分配一个唯一名字 Thread-N;
  • args:以元组的方式,为 target 指定的方法传递参数;
  • kwargs:以字典的方式,为 target 指定的方法传递参数;
  • daemon:指定所创建的线程是否为守护线程。

下面程序演示了如何使用 Thread 类的构造方法创建一个线程:

复制代码
import threading
import time

def test():
    for i in range(5):
        print('test ',i)
        time.sleep(1)
thread = threading.Thread(target=test)
thread.start()
for i in range(5):
    print('main ', i)
    time.sleep(1)

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

如下为代码输出,可以看到主线程和子线程交替执行:

2.2 继承 Thread 类创建线程类

通过继承 Thread 类,我们可以自定义一个线程类,从而实例化该类对象,获得子线程。需要注意的是,在创建 Thread 类的子类时,必须重写从父类继承得到的 run()方法。

复制代码
import threading
 
_#创建子线程类,继承自 Thread 类_
class MyThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self);
    _# 重写run()方法_
    def run(self):
        print("I am %s" % self.name)
 
if __name__ == "__main__":
    for thread in range(0, 5):
        t = MyThread()
        t.start()

这里,线程启动有 start()join() 两种方法。用 start() 方法来启动线程,真正实现了多线程运行,这时无需等待 run 方法体代码执行完毕而直接继续执行后面的代码。

通过调用 Thread 类的 start() 方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到 cpu 时间片,就开始执行 run() 方法;join() 让调用它的线程一直等待直到执行结束(即阻塞调用它的主线程, t 子线程执行结束,主线程才会继续执行)。

在很多情况下,主线程生成并起动了子线程,如果子线程里要进行大量的耗时的运算,主线程往往将于子线程之前结束,但是如果主线程处理完其他的事务后,需要用到子线程的处理结果,也就是主线程需要等待子线程执行完成之后再结束,这个时候就要用到 join()方法了。

这里,我们看一下使用 join() 方法启动线程:

复制代码
import threading
_#定义线程要调用的方法,*add可接收多个以非关键字方式传入的参数_
def action(*add):
    for arc in add:
        _#调用 getName() 方法获取当前执行该程序的线程名_
        print(threading.current_thread().name +" "+ arc)
_#定义为线程方法传入的参数_
my_tuple = ("http://c.biancheng.net/python/",\
            "http://c.biancheng.net/shell/",\
            "http://c.biancheng.net/java/")
_#创建线程_
thread = threading.Thread(target = action,args =my_tuple)
_#启动线程_
thread.start()
_#主线程执行如下语句_
for i in range(5):
    print(threading.current_thread().name)

程序执行结果为(不唯一):

可以看到,我们用 Thread 类创建了一个线程(线程名为 Thread-1),其任务是执行 action() 函数。同时,我们也给主线程 MainThread 安排了循环任务(第 16、17 行)。通过前面的学习我们知道,主线程 MainThread 和子线程 Thread-1 会轮流获得 CPU 资源,因此该程序的输出结果才会向上面显示的这样。

但是,如果我们想让 Thread-1 子线程先执行,然后再让 MainThread 执行第 16、17 行代码,该如何实现呢?很简单,通过调用线程对象的 join() 方法即可。

join() 方法的功能是在程序指定位置,优先让该方法的调用者使用 CPU 资源。该方法的语法格式如下:thread.join( [timeout] )

其中,thread 为 Thread 类或其子类的实例化对象;timeout 参数作为可选参数,其功能是指定 thread 线程最多可以霸占 CPU 资源的时间(以秒为单位),如果省略,则默认直到 thread 执行结束(进入死亡状态)才释放 CPU 资源。

3.确定当前的线程

每一个 Thread 都有一个 name 的属性,代表的就是线程的名字,这个可以在构造方法中赋值。如果在构造方法中没有个 name 赋值的话,默认就是 "Thread-N" 的形式,N 是数字。通过 thread.current_thread() 方法可以返回线程本身,然后就可以访问它的 name 属性。

复制代码
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(threading.current_thread().name+' main ', i)
    time.sleep(1)

如果我们在 Thread 对象创建时,构造方法里面赋值:

复制代码
thread = threading.Thread(target=test,name='TestThread')

4.查询线程是否还在运行

Thread 具有生命周期,创建对象时,代表 Thread 内部被初始化;调用 start() 方法后,thread 会开始运行;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)
        time.sleep(0.5)

thread = threading.Thread(target=test,name='TestThread')
_# thread = threading.Thread(target=test)_
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)

在上面的代码中,我们让 TestThread 比 MainThread 早一点结束,代码运行结果如下。

我们可以看到,主线程通过调用 TestThread 的 isAlive() 方法,准确查询到了它的存活状态。

5.守护线程的创建

Thread 的构造方法中有一个 daemon 参数。默认是 None。那么,daemon 起什么作用呢?我们先看一段示例代码。

复制代码
import threading
import time

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

thread = threading.Thread(target=test,name='TestThread')
_# thread = threading.Thread(target=test,name='TestThread',daemon=True)_
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)

我们让主线程执行代码的时长比 TestThread 要短,程序运行结果如下:

MainThread 没有代码运行的时候,TestThread 还在运行。这是因为 MainThread 在等待其他线程的结束。TestThread 中 daemon 属性默认是 False,这使得 MainThread 需要等待它的结束,自身才结束。

如果要达到,MainThread 结束,子线程也立马结束,怎么做呢?其实很简单,只需要在子线程调用 start() 方法之前设置 daemon 就好了。当然也可以在子线程的构造器中传递 daemon 的值为 True。

修改

复制代码
thread = threading.Thread(target=test,name='TestThread')

复制代码
thread = threading.Thread(target=test,name='TestThread',daemon=True)

可以看到 MainThread 结束了 TestThread 也结束了。也可以用 setDaemon 方法使得只要主线程完成了,不管子线程是否完成,都要和主线程一起退出。

相关推荐
蹦蹦跳跳真可爱58912 分钟前
Python----计算机视觉处理(Opencv:霍夫变换)
人工智能·python·opencv·计算机视觉
程序员柒叔28 分钟前
制作PaddleOCR/PaddleHub的Docker镜像
python·docker·ocr·paddle
喜欢理工科1 小时前
18 C语言标准头文件
c语言·python·算法·c语言标准头文件
PacosonSWJTU1 小时前
python基础-07-模式匹配与正则表达式
python·mysql·正则表达式
程序员总部1 小时前
单例模式在Python中的实现和应用
开发语言·python·单例模式
测试盐1 小时前
django入门教程之cookie和session【六】
后端·python·django
冷琴19961 小时前
基于python+django的商城网站-电子商城管理系统源码+运行
开发语言·python·django
右恩1 小时前
jupyter使用过程中遇到的问题
ide·python·jupyter
zhyoobo2 小时前
使用 Python 训练自己的 AI 模型:从数据预处理到深度学习
人工智能·python·深度学习