我们进行程序开发的时候,肯定避免不了要处理并发的情况。

一般并发的手段有采用多进程和多线程。

但线程比进程更轻量化,系统开销一般也更低,所以大家更倾向于用多线程的方式处理并发的情况。

Python 提供多线程编程的方式。

本文基于 Python3 讲解,Python 实现多线程编程需要借助于 threading 模块。

所以,我们要在代码中引用它。

import threading

threading 模块中最核心的内容是 Thread 这个类。

我们要创建 Thread 对象,然后让它们运行,每个 Thread 对象代表一个线程,在每个线程中我们可以让程序处理不同的任务,这就是多线程编程。

值得注意的是,程序运行时默认就是在主线程上

创建 Thread 对象有 2 种手段。

  1. 直接创建 Thread ,将一个 callable 对象从类的构造器传递进去,这个 callable 就是回调函数,用来处理任务。
  2. 编写一个自定义类继承 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 的生命周期

  1. 创建对象时,代表 Thread 内部被初始化。
  2. 调用 start() 方法后,thread 会开始运行。
  3. 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 类的用法详解相关推荐

  1. Python多线程编程---(1)threading 模块 Thread 类

    全文参考:Python多线程编程(一):threading 模块 Thread 类的用法详解_frank 的专栏-CSDN博客 最近看了下团队自动化测试用例的代码,里面有涉及并行处理的逻辑,主要是基于 ...

  2. python统计csv行数_对Python 多线程统计所有csv文件的行数方法详解

    如下所示: #统计某文件夹下的所有csv文件的行数(多线程) import threading import csv import os class MyThreadLine(threading.Th ...

  3. python six模块详解_对python中的six.moves模块的下载函数urlretrieve详解

    实验环境:windows 7,anaconda 3(python 3.5),tensorflow(gpu/cpu) 函数介绍:所用函数为six.moves下的urllib中的函数,调用如下urllib ...

  4. Python 3.7.1 中 namedtuple 具名元组基本用法详解

    Python 3.7.1 中 namedtuple 具名元组基本用法详解 转载请注明出处:https://blog.csdn.net/jpch89/article/details/84645251 文 ...

  5. java path类_基于java Files类和Paths类的用法(详解)

    Java7中文件IO发生了很大的变化,专门引入了很多新的类: import java.nio.file.DirectoryStream; import java.nio.file.FileSystem ...

  6. string类的用法详解

    //string函数用法详解!附代码,写具体的用法! #include <iostream> #include <string> #include <sstream> ...

  7. python中split啥意思_python中split的用法详解_后端开发

    如何用python正则表达式匹配字符串?_后端开发 用python正则表达式匹配字符串的方法:1.当匹配单个位置的字符串时,可以使用[(.+?)]正则表达式来提取:2.当连续多个位置的字符串匹配时,可 ...

  8. 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 ...

  9. python中values作用_Python values()与itervalues()的用法详解

    dict 对象有一个 values() 方法,这个方法把dict转换成一个包含所有value的list,这样,我们迭代的就是 dict的每一个 value: d = { 'Adam': 95, 'Li ...

最新文章

  1. 一分钟了解负载均衡的一切
  2. Windows Phone 7 SDK 7.1 Beta2 发布
  3. hc05与单片机连接图_单片机科普:单片机的IO口不够用了怎么办?如何扩展单片机的IO口...
  4. 排序算法以及其java实现
  5. H5添加禁止缩放功能
  6. 从源码角度剖析VC6下的内存分配与切割的运作
  7. POJ 2777 ZOJ 1610 HDU 1698 --线段树--区间更新
  8. FFmpeg进行屏幕录像和录音
  9. do_initcalls 的原理
  10. python基本语句大全_python常见语句汇总
  11. 各版本音标 IPA DJ KK 音标对照表
  12. RFID无线射频技术是什么意思
  13. o2o模式的优势 o2o模式的劣势
  14. 导图解文 从梦想到财富(05)世界第八大奇迹,知者赚不知者被赚
  15. Java解析富文本rtf中文乱码
  16. 复现、修复和排查Spring RCE 0day
  17. sodility文档--modifier函数修改器
  18. c++ 消息分发 消息管理
  19. 基于cesium+canvas构建小区内部风场图
  20. LaTex 希腊字母及特殊字符

热门文章

  1. 湖北大学计算机专业在哪个校区,湖北大学怎么样?湖北大学哪个校区好?
  2. 河北省计算机对口试题,河北省对口升学计算机技能试题1
  3. 都2022年了,还有人在吵硬盘分区这事。。。
  4. java基础实现简单的资源下载小工具
  5. Pycharm的快捷键大全
  6. Django中的表单如何使用? Django如何验证前端发来的数据? ✧*。٩(ˊᗜˋ*)و✧*。 Django初体验
  7. 对于测试架构师的简单理解
  8. Word插入代码显示行号并高亮/着色显示
  9. FreeRTOS任务相关API函数---查询/改变某个任务的优先级+获取全部/某个任务状态信息
  10. 阿里云推出网盘App,开放申请!非会员下载 10MB/s!