Python多线程编程(一):threading 模块 Thread 类的用法详解
我们进行程序开发的时候,肯定避免不了要处理并发的情况。
一般并发的手段有采用多进程和多线程。
但线程比进程更轻量化,系统开销一般也更低,所以大家更倾向于用多线程的方式处理并发的情况。
Python 提供多线程编程的方式。
本文基于 Python3 讲解,Python 实现多线程编程需要借助于 threading 模块。
所以,我们要在代码中引用它。
import threading
threading 模块中最核心的内容是 Thread 这个类。
我们要创建 Thread 对象,然后让它们运行,每个 Thread 对象代表一个线程,在每个线程中我们可以让程序处理不同的任务,这就是多线程编程。
值得注意的是,程序运行时默认就是在主线程上
创建 Thread 对象有 2 种手段。
- 直接创建 Thread ,将一个 callable 对象从类的构造器传递进去,这个 callable 就是回调函数,用来处理任务。
- 编写一个自定义类继承 Thread,然后复写 run() 方法,在 run() 方法中编写任务处理代码,然后创建这个 Thread 的子类。
1. 直接创建 Thread 对象。
class threading.Thread(group=None, target=None, name=None, args=(), kwargs={}, *, daemon=None)
Thread 的构造方法中,最重要的参数是 target,所以我们需要将一个 callable 对象赋值给它,线程才能正常运行。
如果要让一个 Thread 对象启动,调用它的 start() 方法就好了。
下面是代码示例。
import threading
import timedef 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 次。
运行结果如下:
test 0
main 0
main 1
test 1
main 2
test 2
main 3
test 3
main 4
test 4
上面的 callable 没有参数,如果需要传递参数的话,args 是固定参数,kwargs 是可变参数。
Thread 的名字
每一个 Thread 都有一个 name 的属性,代表的就是线程的名字,这个可以在构造方法中赋值。
如果在构造方法中没有个 name 赋值的话,默认就是 “Thread-N” 的形式,N 是数字。
import threading
import timedef 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.current_thread() 方法可以返回线程本身,然后就可以访问它的 name 属性。
上面代码运行结果如下:
Thread-1 test 0
MainThread main 0
Thread-1 test 1
MainThread main 1
Thread-1 test 2
MainThread main 2
Thread-1 test 3
MainThread main 3
Thread-1 test 4
MainThread main 4
如果我们在 Thread 对象创建时,构造方法里面赋值。
thread = threading.Thread(target=test,name='TestThread')
那么,运行结果会变成这个样子。
TestThread test 0
MainThread main 0
MainThread main 1
TestThread test 1
MainThread main 2
TestThread test 2
MainThread main 3
TestThread test 3
MainThread main 4
TestThread test 4
Thread 的生命周期
- 创建对象时,代表 Thread 内部被初始化。
- 调用 start() 方法后,thread 会开始运行。
- thread 代码正常运行结束或者是遇到异常,线程会终止。
可以通过 Thread 的 is_alive() 方法查询线程是否还在运行。
值得注意的是,is_alive() 返回 True 的情况是 Thread 对象被正常初始化,start() 方法被调用,然后线程的代码还在正常运行。
import threading
import timedef 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.isAlive())time.sleep(1)
在上面的代码中,我让 TestThread 比 MainThread 早一点结束,代码运行结果如下。
TestThread test 0
MainThread main 0
TestThread is alive True
TestThread test 1
MainThread main 1
TestThread is alive True
TestThread test 2
TestThread test 3
MainThread main 2
TestThread is alive True
TestThread test 4
MainThread main 3
TestThread is alive False
MainThread main 4
TestThread is alive False
我们可以看到,主线程通过调用 TestThread 的 isAlive() 方法,准确查询到了它的存货状态。
join() 提供线程阻塞手段。
上面代码两个线程是同时运行的,但如果让一个先运行,一个后运行,怎么做呢?
调用一个 Thread 的 join() 方法,可以阻塞自身所在的线程。
import threading
import timedef 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.start()
thread.join()for i in range(5):print(threading.current_thread().name+' main ', i)print(thread.name+' is alive ', thread.isAlive())time.sleep(1)
主线程创建了 TestThread 对象后,让其 start,然后通过调用 join() 方法,实现等待。程序运行结果如下:
TestThread test 0
TestThread test 1
TestThread test 2
TestThread test 3
TestThread test 4
MainThread main 0
TestThread is alive False
MainThread main 1
TestThread is alive False
MainThread main 2
TestThread is alive False
MainThread main 3
TestThread is alive False
MainThread main 4
TestThread is alive False
默认的情况是,join() 会一直等待对应线程的结束,但可以通过参数赋值,等待规定的时间就好了。
def join(self, timeout=None):
timeout 是一个浮点参数,单位是秒。
如果我们更改上面的代码。
thread.join(1.0)
它的结果会是这样。
TestThread test 0
TestThread test 1
MainThread main 0
TestThread is alive True
TestThread test 2
TestThread test 3
MainThread main 1
TestThread is alive True
TestThread test 4
MainThread main 2
TestThread is alive False
MainThread main 3
TestThread is alive False
MainThread main 4
TestThread is alive False
主线程只等待了 1 秒钟。
Thread 中的 daemon 属性
有同学可能会注意到,Thread 的构造方法中有一个 daemon 参数。默认是 None。
那么,daemon 起什么作用呢?
我们先看一段示例代码。
import threading
import timedef 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.isAlive())time.sleep(1)
我们让主线程执行代码的时长比 TestThread 要短。
程序运行结果如下。
TestThread test 0
MainThread main 0
TestThread is alive True
MainThread main 1
TestThread is alive True
TestThread test 1
MainThread main 2
TestThread is alive True
MainThread main 3
TestThread is alive True
TestThread test 2
MainThread main 4
TestThread is alive True
TestThread test 3
TestThread test 4
MainThread 没有代码运行的时候,TestThread 还在运行。
这是因为 MainThread 在等待其他线程的结束。
TestThread 中 daemon 属性默认是 False,这使得 MainThread 需要等待它的结束,自身才结束。
如果要达到,MainThread 结束,子线程也立马结束,怎么做呢?
其实很简单,只需要在子线程调用 start() 方法之前设置 daemon 就好了。
当然也可以在子线程的构造器中传递 daemon 的值为 True。
thread = threading.Thread(target=test,name='TestThread',daemon=True)
# thread.setDaemon(True)
更改前面代码示例,运行结果如下
TestThread test 0
MainThread main 0
TestThread is alive True
MainThread main 1
TestThread is alive True
TestThread test 1
MainThread main 2
TestThread is alive True
MainThread main 3
TestThread is alive True
TestThread test 2
MainThread main 4
TestThread is alive True
可以看到 MainThread 结束了 TestThread 也结束了。
2.自定义类继承 Thread
前面讲过,直接初始化一个 Thread,然后,现在还有一种方式就是自定义一个 Thread 的子类,然后复写它的 run() 方法。
import threading
import timeclass TestThread(threading.Thread):def __init__(self,name=None):threading.Thread.__init__(self,name=name)def run(self):for i in range(5):print(threading.current_thread().name + ' test ', i)time.sleep(1)thread = TestThread(name='TestThread')
thread.start()for i in range(5):print(threading.current_thread().name+' main ', i)print(thread.name+' is alive ', thread.isAlive())time.sleep(1)
上面的代码,我们自定义了 TestThread 这个类,然后继承了 threading.Thread。
只有在 run() 方法中处理逻辑。最终代码运行结果如下:
TestThread test 0
MainThread main 0
TestThread is alive True
TestThread test 1
MainThread main 1
TestThread is alive True
TestThread test 2
MainThread main 2
TestThread is alive True
MainThread main 3
TestThread is alive True
TestThread test 3
MainThread main 4
TestThread test 4
TestThread is alive True
这与之前的效果并无差异,但我还是推荐用这种方法,毕竟面向对象编程嘛。
自此,Python 多线程编码技术就大致介绍完毕,大家可以进行实际代码编写了。
但是,多线程编程的难点在于多个线程之间共享数据的同步,这是非常容易出错的地方,我将分别编写相应的博文去介绍一些高级的技术点。
Python多线程编程(一):threading 模块 Thread 类的用法详解相关推荐
- Python多线程编程---(1)threading 模块 Thread 类
全文参考:Python多线程编程(一):threading 模块 Thread 类的用法详解_frank 的专栏-CSDN博客 最近看了下团队自动化测试用例的代码,里面有涉及并行处理的逻辑,主要是基于 ...
- python统计csv行数_对Python 多线程统计所有csv文件的行数方法详解
如下所示: #统计某文件夹下的所有csv文件的行数(多线程) import threading import csv import os class MyThreadLine(threading.Th ...
- python six模块详解_对python中的six.moves模块的下载函数urlretrieve详解
实验环境:windows 7,anaconda 3(python 3.5),tensorflow(gpu/cpu) 函数介绍:所用函数为six.moves下的urllib中的函数,调用如下urllib ...
- Python 3.7.1 中 namedtuple 具名元组基本用法详解
Python 3.7.1 中 namedtuple 具名元组基本用法详解 转载请注明出处:https://blog.csdn.net/jpch89/article/details/84645251 文 ...
- java path类_基于java Files类和Paths类的用法(详解)
Java7中文件IO发生了很大的变化,专门引入了很多新的类: import java.nio.file.DirectoryStream; import java.nio.file.FileSystem ...
- string类的用法详解
//string函数用法详解!附代码,写具体的用法! #include <iostream> #include <string> #include <sstream> ...
- python中split啥意思_python中split的用法详解_后端开发
如何用python正则表达式匹配字符串?_后端开发 用python正则表达式匹配字符串的方法:1.当匹配单个位置的字符串时,可以使用[(.+?)]正则表达式来提取:2.当连续多个位置的字符串匹配时,可 ...
- for in在python中什么意思_Python for i in range ()用法详解
for i in range ()作用: range()是一个函数, for i in range () 就是给i赋值: 比如 for i in range (1,3): 就是把1,2依次赋值给i r ...
- python中values作用_Python values()与itervalues()的用法详解
dict 对象有一个 values() 方法,这个方法把dict转换成一个包含所有value的list,这样,我们迭代的就是 dict的每一个 value: d = { 'Adam': 95, 'Li ...
最新文章
- 一分钟了解负载均衡的一切
- Windows Phone 7 SDK 7.1 Beta2 发布
- hc05与单片机连接图_单片机科普:单片机的IO口不够用了怎么办?如何扩展单片机的IO口...
- 排序算法以及其java实现
- H5添加禁止缩放功能
- 从源码角度剖析VC6下的内存分配与切割的运作
- POJ 2777 ZOJ 1610 HDU 1698 --线段树--区间更新
- FFmpeg进行屏幕录像和录音
- do_initcalls 的原理
- python基本语句大全_python常见语句汇总
- 各版本音标 IPA DJ KK 音标对照表
- RFID无线射频技术是什么意思
- o2o模式的优势 o2o模式的劣势
- 导图解文 从梦想到财富(05)世界第八大奇迹,知者赚不知者被赚
- Java解析富文本rtf中文乱码
- 复现、修复和排查Spring RCE 0day
- sodility文档--modifier函数修改器
- c++ 消息分发 消息管理
- 基于cesium+canvas构建小区内部风场图
- LaTex 希腊字母及特殊字符
热门文章
- 湖北大学计算机专业在哪个校区,湖北大学怎么样?湖北大学哪个校区好?
- 河北省计算机对口试题,河北省对口升学计算机技能试题1
- 都2022年了,还有人在吵硬盘分区这事。。。
- java基础实现简单的资源下载小工具
- Pycharm的快捷键大全
- Django中的表单如何使用? Django如何验证前端发来的数据? ✧*。٩(ˊᗜˋ*)و✧*。 Django初体验
- 对于测试架构师的简单理解
- Word插入代码显示行号并高亮/着色显示
- FreeRTOS任务相关API函数---查询/改变某个任务的优先级+获取全部/某个任务状态信息
- 阿里云推出网盘App,开放申请!非会员下载 10MB/s!