1、概述

在Python3中主要有3个线程模块,即:_thread、threading、queue模块;
_thread模块:在 3.7 版进行了更改,这个模块曾经是可选的,但现在总是可用的,之前叫thread。
_thread模块:提供了操作多个线程(也被称为 轻量级进程 或 任务)的底层原语 —— 多个控制线程共享全局数据空间。为了处理同步问题,也提供了简单的锁机制(也称为 互斥锁 或 二进制信号)。
threading 模块:这个模块在较低级的模块 _thread 基础上建立较高级的线程接口。
queue模块:实现了多生产者、多消费者队列。这特别适用于消息必须安全地在多线程间交换的线程编程。模块中的 Queue 类实现了所有所需的锁定语义。

本文案例在python3.8环境下运行。

2、_thread模块应用

使用这个模块之前,先导入它:

import _thread

如何来创建一个线程?
通过文档我们找到一个函数:

_thread.start_new_thread(function, args[, kwargs])

函数解释:
开启一个新线程并返回其标识。
function :线程执行函数;
args :附带参数列表 (必须是元组)。
kwargs :可选的 ,参数指定一个关键字参数字典。
当函数返回时,线程会静默地退出。
这与java类似,线程内语句执行完,线程会自动结束并退出。
当函数因某个未处理异常而终结时,sys.unraisablehook() 会被调用以处理异常。
钩子参数的 object 属性为 function。 在默认情况下,会打印堆栈回溯然后该线程将退出(但其他线程会继续运行)。
当函数引发 SystemExit 异常时,它会被静默地忽略。

举个例子:

import _thread
import time# 自定义的线程内调用的函数
def show(threadName, delaySec, times):while times > 0:time.sleep(delaySec)times -= 1print("%s: %s" % (threadName, time.ctime(time.time())))_thread.start_new_thread(show, ("Thread-1", 1, 5))
_thread.start_new_thread(show, ("Thread-2", 1, 4))time.sleep(10)  # 让主线程休眠10秒,足够子线程完成

当我们在主线程中使用start_new_thread创建新的线程的时,主线程无法得子知线程何时结束,导致主线程已经执行完了,子线程却还未完成,因此会使用这句time.sleep(10) 。

输出结果:

Thread-1: Sat Mar 21 10:52:35 2020
Thread-1: Sat Mar 21 10:52:35 2020
Thread-2: Sat Mar 21 10:52:35 2020
Thread-1: Sat Mar 21 10:52:36 2020
Thread-1: Sat Mar 21 10:52:36 2020
Thread-2: Sat Mar 21 10:52:36 2020
Thread-2: Sat Mar 21 10:52:37 2020
Thread-2: Sat Mar 21 10:52:38 2020

可以看到Thread-1和Thread-2交替打印,每次运行的结果都会有很大概率的不同。

前面例子提到 time.sleep(10) 使用主线成延时来等待子线程执行完成。但是有一些缺陷,我们需要大概估计子线程执行的时间然后设置延时,万一我们执行的子线程耗时不可估计,那么这时候使用延时这种方式就不太准确,因此我们采用另外一种方式,引入锁的概念,就是保证让子线程执行前加锁,执行完成释放锁,主线程根据锁的状态判断子线程是否执行完成来决定是否退出程序。

举个例子:
import _thread
import time# 自定义的线程内调用的函数
def show(threadName, delaySec, times, lock):while times > 0:time.sleep(delaySec)times -= 1print("%s: %s" % (threadName, time.ctime(time.time())))# 释放锁lock.release()myLock = _thread.allocate_lock()  # 获取locktype对象
# 获取锁
myLock.acquire(1, 1)_thread.start_new_thread(show, ("Thread-1", 0.5, 4, myLock))
# 可以试试放开下面这句话,同时运行两个线程,会导致输出结果并不全,解决这个问题我们可以使用threading模块
# _thread.start_new_thread(show, ("Thread-2", 0.5, 4, myLock))# 获取锁的状态,当锁一直被子线程占用时,程序处于等待状态
while myLock.locked():pass
输出结果:
Thread-1: Sat Mar 21 11:29:53 2020
Thread-1: Sat Mar 21 11:29:54 2020
Thread-1: Sat Mar 21 11:29:54 2020
Thread-1: Sat Mar 21 11:29:55 2020Process finished with exit code 0

如果创建两个及以上的线程会导致输出结果不是预期的结果,要解决这个问题我们需要引入threading模块来解决这个问题

3、threading模块应用

使用这个模块之前,先导入它:

import threading

创建并启动线程:

举个例子:
import threading
import time# 通过继承实现一个自己的线程类,类似java 的runnable
class MyThread(threading.Thread):def __init__(self, threadId, threadName, delaySec, times):threading.Thread.__init__(self)self.threadId = threadIdself.threadName = threadNameself.delaySec = delaySecself.times = times# 重载threading.Thread 的run 方法,类似 java 实现 runnable 后实现run 方法def run(self):print("线程%s:run->开始" % (self.threadName))show(self.threadId, self.threadName, self.delaySec, self.times)print("线程%s:run->结束" % (self.threadName))# 自定义的线程内调用的函数
def show(threadId, threadName, delaySec, times):while times > 0:time.sleep(delaySec)times -= 1print("%s: %s: %s" % (threadId, threadName, time.ctime(time.time())))myThread = MyThread(1, "Thread-1", 0.5, 5)
myThread2 = MyThread(2, "Thread-2", 1, 5)
myThread.start()
myThread2.start()
# 将两个子线程加入主线程,等待子线程中止后再退出主线程。
# 应用场景:主线程生成并启动了子线程,而子线程里要进行大量的耗时的运算(这里可以借鉴下线程的作用),当主线程处理完其他的事务后,需要用到子线程的处理结果,这个时候就要用到join();方法了。
myThread.join()
myThread2.join()print ("退出主线程")

例子中使用了join函数,什么时候使用它?

应用场景:主线程生成并启动了子线程,而子线程里要进行大量的耗时的运算(这里可以借鉴下线程的作用),当主线程处理完其他的事务后,需要用到子线程的处理结果,这个时候就要用到join()方法。

主线程:即每一个应用程序启动后都会创建一个主线程,在这个主线程中再创建的线程都叫子线程。

输出结果:
线程Thread-1:run->开始
线程Thread-2:run->开始
1: Thread-1: Sat Mar 21 12:03:17 2020
2: Thread-2: Sat Mar 21 12:03:18 2020
1: Thread-1: Sat Mar 21 12:03:18 2020
1: Thread-1: Sat Mar 21 12:03:18 2020
2: Thread-2: Sat Mar 21 12:03:19 2020
1: Thread-1: Sat Mar 21 12:03:19 2020
1: Thread-1: Sat Mar 21 12:03:19 2020
线程Thread-1:run->结束
2: Thread-2: Sat Mar 21 12:03:20 2020
2: Thread-2: Sat Mar 21 12:03:21 2020
2: Thread-2: Sat Mar 21 12:03:22 2020
线程Thread-2:run->结束
退出主线程Process finished with exit code 0

可以看出线程一和线程而随机交替执行。
接下来我们需要解决上一节我们提到的,_thread模块在两个及以上的情况下,准确判断子线程是否都执行完,如果执行完则退出程序:

举个例子:
import threading
import time# 实现一个自己的线程类,类似java 的runnable
class MyThread(threading.Thread):def __init__(self, threadId, threadName, delaySec, times):threading.Thread.__init__(self)self.threadId = threadIdself.threadName = threadNameself.delaySec = delaySecself.times = times# 重载threading.Thread 的run 方法,类似 java 实现 runnable 后实现run 方法def run(self):print("线程%s:run->开始" % (self.threadName))# 获取锁threadLock.acquire()show(self.threadId, self.threadName, self.delaySec, self.times)# 释放锁,开启下一个线程threadLock.release()print("线程%s:run->结束" % (self.threadName))# 自定义的线程内调用的函数
def show(threadId, threadName, delaySec, times):while times > 0:time.sleep(delaySec)times -= 1print("%s: %s: %s" % (threadId, threadName, time.ctime(time.time())))# 获取线程锁对象
threadLock = threading.Lock()
threads = []myThread = MyThread(1, "Thread-1", 0.5, 5)
myThread2 = MyThread(2, "Thread-2", 1, 5)threads.append(myThread)
threads.append(myThread2)myThread.start()
myThread2.start()# 等待所有线程完成
for t in threads:t.join()print("退出主线程")
输出结果:
线程Thread-1:run->开始
线程Thread-2:run->开始
1: Thread-1: Sat Mar 21 12:11:35 2020
1: Thread-1: Sat Mar 21 12:11:36 2020
1: Thread-1: Sat Mar 21 12:11:36 2020
1: Thread-1: Sat Mar 21 12:11:37 2020
1: Thread-1: Sat Mar 21 12:11:37 2020
线程Thread-1:run->结束
2: Thread-2: Sat Mar 21 12:11:38 2020
2: Thread-2: Sat Mar 21 12:11:39 2020
2: Thread-2: Sat Mar 21 12:11:40 2020
2: Thread-2: Sat Mar 21 12:11:41 2020
2: Thread-2: Sat Mar 21 12:11:42 2020
线程Thread-2:run->结束
退出主线程Process finished with exit code 0

通过这个输出结果我们看到了线程同步的效果,且保证子线程执行完后结束主线程。

特别提示:如果我们不想实现同步效果,继续保持异步,则只需要注释threadLock这个锁变量相关的3行代码。

4、queue模块应用

线程优队列
使用这个模块之前,先导入它:

import queue

queue模块实现了三种类型的队列,它们的区别仅仅是条目取回的顺序。

  • FIFO 队列:先添加的任务先取回;
  • LIFO 队列:最近被添加的条目先取回(操作类似一个堆栈);
  • 优先级队:条目将保持排序( 使用 heapq 模块 ) 并且最小值的条目第一个返回。
举一个优先级队列的例子:
import queue
import threading
import timeexitFlag = 0# 实现一个自己的线程类,类似java 的runnable
class MyThread(threading.Thread):def __init__(self, threadId, threadName,  que):threading.Thread.__init__(self)self.threadId = threadIdself.threadName = threadNameself.que = que# 重载threading.Thread 的run 方法,类似 java 实现 runnable 后实现run 方法def run(self):print("线程%s:run->开始" % (self.threadName))show(self.threadId, self.threadName,  self.que)print("线程%s:run->结束" % (self.threadName))# 自定义的线程内调用的函数
def show(threadId, threadName,  que):while not exitFlag:queueLock.acquire()if not workQueue.empty():data = que.get()queueLock.release()print("%s: %s: %s,传递的数据:%s" % (threadId, threadName, time.ctime(time.time()), data))else:queueLock.release()threadNameList = ["Thread-1", "Thread-2", "Thread-3", "Thread-4"]
nameList = ["One1", "Two2", "Three3", "Four4",'Five5','Six6','Seven7']
# 获取线程锁对象
queueLock = threading.Lock()
# 创建10个线程的队列空间
workQueue = queue.Queue(10)
threads = []
threadId = 1;for threadName in threadNameList:myThread = MyThread(threadId, threadName, workQueue)myThread.start()threads.append(myThread)threadId += 1queueLock.acquire()
for name in nameList:workQueue.put(name)
queueLock.release()# 等待队列清空
while not workQueue.empty():pass# 通知线程是时候退出
exitFlag = 1# 等待所有线程完成
for t in threads:t.join()print("退出主线程")
输出结果:
线程Thread-1:run->开始
线程Thread-2:run->开始
线程Thread-3:run->开始
线程Thread-4:run->开始
2: Thread-2: Sat Mar 21 14:11:11 2020,传递的数据:One1
3: Thread-3: Sat Mar 21 14:11:11 2020,传递的数据:Two2
3: Thread-3: Sat Mar 21 14:11:11 2020,传递的数据:Three3
3: Thread-3: Sat Mar 21 14:11:11 2020,传递的数据:Four4
3: Thread-3: Sat Mar 21 14:11:11 2020,传递的数据:Five5
3: Thread-3: Sat Mar 21 14:11:11 2020,传递的数据:Six6
3: Thread-3: Sat Mar 21 14:11:11 2020,传递的数据:Seven7
线程Thread-3:run->结束
线程Thread-1:run->结束
线程Thread-4:run->结束线程Thread-2:run->结束退出主线程Process finished with exit code 0

例子创建了四个子线程输出,使用的队列容量为10,将nameList 数据放入队列,按照放入的顺序输出,并且nameList[0]第一个值先返回。

5、总结:

_thread不太推荐使用了,尤其是在多线程的时候,会出现预期的结果不正确。
推荐使用threading模块,处理多线程问题会得到我们想要的预期结果。
join() 这个函数是保证子线程执行完之后再退出主线程,防止内存泄漏或主线程执行完了子线程还未执行完的情况。
在调用.acquire()和.release()两个函数时IDE没有代码自动提示,比较奇怪。

参考链接:queue模块
参考链接:_thread模块
参考链接:threading模块

(十五)Python中级知识-线程模块相关推荐

  1. 十五. Python基础(15)--内置函数-1

    十五. Python基础(15)--内置函数-1 1 ● eval(), exec(), compile() 执行字符串数据类型的python代码 检测#import os 'import' in c ...

  2. STC8H开发(十五): GPIO驱动Ci24R1无线模块

    目录 STC8H开发(一): 在Keil5中配置和使用FwLib_STC8封装库(图文详解) STC8H开发(二): 在Linux VSCode中配置和使用FwLib_STC8封装库(图文详解) ST ...

  3. 趁着课余时间学点Python(十五)有趣的小模块

    一.Python模块与包 python中模块与包的含义 1. Python模块与包 模块是单个的py文件,包是包含多个py文件的文件夹 1.1 模块 模块是一个py文件 我们自己创建一个py文件就是一 ...

  4. python中xml模块_python学习第十五天-2(XML模块)

    也是一种文本转换形式. import xxxxxxxxxxxxxxxxxxx  as xx,可以用xx代替xxxxxxxxxxxxxxxxxxx模块 xml文件的新增,修改,删除,查询. 新增:​ i ...

  5. [网络安全自学篇] 十五.Python攻防之多线程、C段扫描和数据库编程(二)

    这是作者的系列网络安全自学教程,主要是关于网安工具和实践操作的在线笔记,特分享出来与博友共勉,希望您们喜欢,一起进步.前文分享了Python网络攻防相关基础知识,包括正则表达式.Web编程和套接字通信 ...

  6. python基础知识专题 - 模块的打包和发布

    分发Python模块 jcLee95 的 CSDN 博客 邮箱 :291148484@163.com CSDN 主页:https://blog.csdn.net/qq_28550263?spm=100 ...

  7. 会议交流 | 第十五届全国知识图谱与语义计算大会(CCKS 2021)12月25日线上召开...

    勘误:张伟老师为华东师范大学紫江青年学者 OpenKG OpenKG(中文开放知识图谱)旨在推动以中文为核心的知识图谱数据的开放.互联及众包,并促进知识图谱算法.工具及平台的开源开放. 点击阅读原文, ...

  8. python中级第九课--模块和包(小白piao分享)

    模块和包 概念: 模块:每个独立的py文件         包:描述模块组织构成         例如: #包: package1/__init__.pysub_package1/__init__.p ...

  9. 三十五 Python分布式爬虫打造搜索引擎Scrapy精讲—scrapy分布式爬虫要点

    1.分布式爬虫原理 2.分布式爬虫优点 3.分布式爬虫需要解决的问题 转载于:https://www.cnblogs.com/meng-wei-zhi/p/8182813.html

  10. 【黑金原创教程】【FPGA那些事儿-驱动篇I 】实验二十五:SDHC模块

    实验二十五:SDHC模块 笔者曾经说过,SD卡发展至今已经衍生许多版本,实验二十四就是针对版本SDV1.×的SD卡.实验二十四也说过,CMD24还有CMD17会故意偏移地址29,让原本范围指向从原本的 ...

最新文章

  1. 在线作图|在线做扩增子抽平
  2. Endnote X8云同步:家里单位实时同步文献笔记,有网随时读文献
  3. 子串在主机中出现的位置indexOf()--简单
  4. C++内存分配方式详解——堆、栈、自由存储区、全局/静态存储区和常量存储区
  5. Linux信号量之用户态信号量(Posix信号量->无名信号量)
  6. typecho博客主题 “Freedom-冷文”
  7. 在IE中用js改变table的innerHTML属性报“未知的运行时错误”
  8. 百度杀毒软件2013正式发布
  9. Matlab数理统计工具箱应用简介(转)
  10. Android 6.0 sensor 框架详解 (application层)
  11. sniffer pro 4.7.5安装教程(附安装系统环境及软件链接)
  12. networkx节点显示、节点中心性度量
  13. 微信红包封面,你真的领取到了吗?
  14. napa与matlab,纳帕谷产区Napa Valley|酒斛网 - 与数十万葡萄酒爱好者一起发现美酒,分享微醺的乐趣...
  15. 有师傅小程序开源版v2.4.14(含后端源码)
  16. 医学图像中的窗宽、窗位
  17. Formula One 常用函数及属性
  18. access有效性规则不为空值_Access 有效性规则/验证规则
  19. Qlikview---日期字段
  20. 还儿童一个健康上网环境,正式开启我的路由器URL网址白名单之旅

热门文章

  1. kali流量转发后依然断网_虚拟运营商流量卡列表
  2. 机器学习之线性回归 Linear Regression(二)Python实现
  3. 由浅入深学习android input系统(五) - input系统的启动
  4. todo已完成任务_总结一下TODO的用法
  5. Istio入坑指南(二) Istio的安装与简单的使用
  6. 猿创征文|人工智能啾养成之路 - 写代码三天,CSDN治好了我的精神内耗
  7. Ubuntu16.04 与本地Win7共享文件夹
  8. 【复习】Listening and Reading Comprehension
  9. 医学图像自适应计算图像窗宽窗位技巧
  10. echarts+DataV的用法