一文速通Python并行盘算:02 Python多线程编程-threading模块、线程的创建 ...

打印 上一主题 下一主题

主题 966|帖子 966|积分 2898

一文速通 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__() 构造器,可以用来创建线程:
  1. __init__(self, group=None, target=None, name=None, args=(), kwargs=None, *,daemon=None)
复制代码
此构造方法中,以上所有参数都是可选参数,即可以使用,也可以忽略。此中各个参数的含义如下:

  • group:指定所创建的线程隶属于哪个线程组;
  • target:当线程启动的时候要实行的函数;
  • name: 线程的名字,默认会分配一个唯一名字 Thread-N;
  • args:以元组的方式,为 target 指定的方法传递参数;
  • kwargs:以字典的方式,为 target 指定的方法传递参数;
  • daemon:指定所创建的线程是否为守护线程。
下面程序演示了如何使用 Thread 类的构造方法创建一个线程:
  1. import threading
  2. import time
  3. def test():
  4.     for i in range(5):
  5.         print('test ',i)
  6.         time.sleep(1)
  7. thread = threading.Thread(target=test)
  8. thread.start()
  9. for i in range(5):
  10.     print('main ', i)
  11.     time.sleep(1)
复制代码
上面代码很简单,在主线程上打印 5 次,在一个子线程上打印 5 次。
如下为代码输出,可以看到主线程和子线程瓜代实行:

2.2 继承 Thread 类创建线程类

通过继承 Thread 类,我们可以自界说一个线程类,从而实例化该类对象,得到子线程。需要注意的是,在创建 Thread 类的子类时,必须重写从父类继承得到的 run()方法。
  1. import threading
  2. _#创建子线程类,继承自 Thread 类_
  3. class MyThread(threading.Thread):
  4.     def __init__(self):
  5.         threading.Thread.__init__(self);
  6.     _# 重写run()方法_
  7.     def run(self):
  8.         print("I am %s" % self.name)
  9. if __name__ == "__main__":
  10.     for thread in range(0, 5):
  11.         t = MyThread()
  12.         t.start()
复制代码

这里,线程启动有 start() 和 join() 两种方法。用 start() 方法来启动线程,真正实现了多线程运行,这时无需等候 run 方法体代码实行完毕而直接继承实行后面的代码。
通过调用 Thread 类的 start() 方法来启动一个线程,这时此线程处于就绪(可运行)状态,并没有运行,一旦得到 cpu 时间片,就开始实行 run() 方法;join() 让调用它的线程一直等候直到实行竣事(即阻塞调用它的主线程, t 子线程实行竣事,主线程才会继承实行)。
在很多环境下,主线程天生并起动了子线程,假如子线程里要举行大量的耗时的运算,主线程往往将于子线程之前竣事,但是假如主线程处置惩罚完其他的事务后,需要用到子线程的处置惩罚结果,也就是主线程需要等候子线程实行完成之后再竣事,这个时候就要用到 join()方法了。
这里,我们看一下使用 join() 方法启动线程:
  1. import threading
  2. _#定义线程要调用的方法,*add可接收多个以非关键字方式传入的参数_
  3. def action(*add):
  4.     for arc in add:
  5.         _#调用 getName() 方法获取当前执行该程序的线程名_
  6.         print(threading.current_thread().name +" "+ arc)
  7. _#定义为线程方法传入的参数_
  8. my_tuple = ("http://c.biancheng.net/python/",\
  9.             "http://c.biancheng.net/shell/",\
  10.             "http://c.biancheng.net/java/")
  11. _#创建线程_
  12. thread = threading.Thread(target = action,args =my_tuple)
  13. _#启动线程_
  14. thread.start()
  15. _#主线程执行如下语句_
  16. for i in range(5):
  17.     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 属性。
  1. import threading
  2. import time
  3. def test():
  4.     for i in range(5):
  5.         print(threading.current_thread().name+' test ',i)
  6.         time.sleep(1)
  7. thread = threading.Thread(target=test)
  8. thread.start()
  9. for i in range(5):
  10.     print(threading.current_thread().name+' main ', i)
  11.     time.sleep(1)
复制代码

假如我们在 Thread 对象创建时,构造方法里面赋值:
  1. thread = threading.Thread(target=test,name='TestThread')
复制代码

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

Thread 具有生命周期,创建对象时,代表 Thread 内部被初始化;调用 start() 方法后,thread 会开始运行;thread 代码正常运行竣事或者是遇到异常,线程会终止。
可以通过 Thread 的 is_alive()方法查询线程是否还在运行。值得注意的是,is_alive() 返回 True 的环境是 Thread 对象被正常初始化,start()方法被调用,然后线程的代码还在正常运行。
  1. import threading
  2. import time
  3. def test():
  4.     for i in range(5):
  5.         print(threading.current_thread().name+' test ',i)
  6.         time.sleep(0.5)
  7. thread = threading.Thread(target=test,name='TestThread')
  8. _# thread = threading.Thread(target=test)_
  9. thread.start()
  10. for i in range(5):
  11.     print(threading.current_thread().name+' main ', i)
  12.     print(thread.name+' is alive ', thread.is_alive())
  13.     time.sleep(1)
复制代码
在上面的代码中,我们让 TestThread 比 MainThread 早一点竣事,代码运行结果如下。

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

Thread 的构造方法中有一个 daemon 参数。默认是 None。那么,daemon 起什么作用呢?我们先看一段示例代码。
  1. import threading
  2. import time
  3. def test():
  4.     for i in range(5):
  5.         print(threading.current_thread().name+' test ',i)
  6.         time.sleep(2)
  7. thread = threading.Thread(target=test,name='TestThread')
  8. _# thread = threading.Thread(target=test,name='TestThread',daemon=True)_
  9. thread.start()
  10. for i in range(5):
  11.     print(threading.current_thread().name+' main ', i)
  12.     print(thread.name+' is alive ', thread.is_alive())
  13.     time.sleep(1)
复制代码
我们让主线程实行代码的时长比 TestThread 要短,程序运行结果如下:

MainThread 没有代码运行的时候,TestThread 还在运行。这是因为 MainThread 在等候其他线程的竣事。TestThread 中 daemon 属性默认是 False,这使得 MainThread 需要等候它的竣事,自身才竣事。
假如要到达,MainThread 竣事,子线程也立马竣事,怎么做呢?其实很简单,只需要在子线程调用 start() 方法之前设置 daemon 就好了。当然也可以在子线程的构造器中传递 daemon 的值为 True。
修改
  1. thread = threading.Thread(target=test,name='TestThread')
复制代码
  1. thread = threading.Thread(target=test,name='TestThread',daemon=True)
复制代码

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

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

欢乐狗

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表