多线程理解

多线程是多个任务同时运行的一种方式。比如一个循环中,每个循环看做一个任务,我们希望第一次循环运行还没结束时,就可以开始第二次循环,用这种方式来节省时间。

python中这种同时运行的目的是最大化利用CPU的计算能力,将很多等待时间利用起来。这也说明如果程序耗时不是因为等待时间,而是任务非常多,就是要计算那么久,则多线程无法改善运行时间。

更多有关多线程理解的内容可以参考下面资料

简单使用

先看下面这个函数

import time
def myfun():
time.sleep(1)
a = 1 + 1
print(a)

如果我们要运行10次这个函数,它的运行时间主要在于每次sleep的那一秒,1 + 1的计算是不会耗多少时间的。这种情况可以用多线程提高效率。

下面来看一下不使用多线程耗时和使用多线程的耗时

不使用多线程

t = time.time()
for _ in range(5):
myfun()
print(time.time() - t)

得到结果是 5.002434492111206

下面我们使用多线程

from threading import Thread
for _ in range(5):
th = Thread(target = myfun)
th.start()

这样使用多线程其实就可以了,你会发现大概1秒,5个2会同时出来,说明5次循环其实几乎同时运行,5次1秒的等待时间同时进行,最后只等待了1秒。

这里多线程只包括了两步

  • Thread增加一个线程,这里是将每一次循环作为一次新的线程,一个线程执行一次myfun函数。
  • start()开始运行这个线程,每个线程都需要这样显式开启才会运行。一个线程这样开启后,不需要等待它运行完成,就可以继续运行下面的程序,即下一次循环(然后又新建了第二个线程,运行未结束即开启第三个……)

这里要注意一点:多线程是放在循环里面的,不能定义好循环之后,从外面将它变成多线程。

读者可能会注意到,不用多线程时是通过程序计算时间的,使用多线程却没有。这是因为要计算时间需要增加一些代码,无法展示最简单的多线程使用,所以就先不计算时间。接下来我们就讲join()的使用,并计算时间。

join的使用

线程的join()方法表示等这个线程运行完毕,程序再往下运行。我们来看下面的例子

from threading import Thread
t = time.time()
for _ in range(5):
th = Thread(target = myfun)
th.start()
th.join()
print(time.time() - t)
# 结果为 5.0047078132629395 秒

这里start()之后马上join(),表示每一个线程都要运行结束才能进行下一次循环,这样就和没有使用多线程没有区别了。不过如果要计算多线程运行时间却是要用到这个join()

我们先看一下不用join()的情况

from threading import Thread
t = time.time()
for _ in range(5):
th = Thread(target = myfun)
th.start()
print(time.time() - t)
# 结果为 0.0009980201721191406 秒

它连1秒都没有等,就输出了结果,而且5个2是在打印出这个之后才输出出来的。这是因为print(time.time() - t)是区别于那5次循环线程之外的第6个线程,它不会等待5个线程运行结束就会开始运行。所以这样是无法获得上面5个线程的运行时间的,我们需要用join()等待5个线程都运行结束。

代码如下

from threading import Thread
t = time.time()
ths = []
for _ in range(5):
th = Thread(target = myfun)
th.start()
ths.append(th)
for th in ths:
th.join()
print(time.time() - t)
# 结果为 1.0038363933563232

上面定义ths列表存储这些线程,最后用循环确保每一个线程都已经运行完成再计算时间差。

join()不只是用于这种情形。当一步代码运行依赖之前代码运行完成时,就要加入join()命令。

现在我们已经学完了多线程的一般使用方法,可以在多数场景使用了。下面来介绍一些细节

其他

(1)线程名称

我们直接看下面的代码

import threading
print(threading.current_thread().getName())
def myfun():
time.sleep(1)
print(threading.current_thread().name)
a = 1 + 1
for i in range(5):
th = threading.Thread(target = myfun, name = 'thread {}'.format(i))
th.start()
# 输出结果
MainThread
thread 0
thread 1
thread 4
thread 3
thread 2

解释一下

  • threading.current_thread()表示当前线程,可以调用namegetName()获取线程名称
  • 任何进程都会默认启动一个线程,默认名称为MainThread,也就是主程序占一个线程,这个线程和之后用Thread新加的线程是相互独立的,主线程不会等待其余线程运行结束就会继续往下运行。之前不用join()无法计算运行时间就是因为主线程先运行完了。
  • Thread表示运行这个函数启动一个新的线程,在其中加一个name参数指定这个函数线程名,则在这个函数内打印线程名就显示这里name参数对应值
  • 在循环中打印有两种。第一种print(threading.current_thread().name)则是MainThread;第二种print(th.name)则是thread 1

(2)Thread函数

上面我们使用了Thread函数的target name参数,下面来说一下它的其他参数

  • args指定target对应函数的参数,用元组传入,比如args = (3, )
  • daemon主线程默认是False,如果没有指定则继承父线程的值。True则如果主线程运行结束,该线程也停止运行;False则该线程会继续运行直到运行结束,无视主线程如何。(要看这个参数的效果要在py文件中编写代码,在cmd里运行,不能在jupyter notebook里,因为这里会多出一些线程干扰)
  • group是预留的一个参数,用于以后扩展ThreadGroup类,现在没用

(3)Thread对象

上面threading.Threadthreading.current_thread()都创建了一个Thread对象,Thread对象有如下属性和方法

  • getName() .name 获取线程名
  • setName() 设置线程名
  • start() join()这两个之前说过了
  • join()有一个timeout参数,表示等待这个线程结束时,如果等待时间超过这个时间,就不再等,继续进行下面的代码,但是这个线程不会被中断
  • run() 也是运行这个线程,但是必须等到这个线程运行结束才会继续执行之后的代码(如果将上面的start全换成run则相当于没有开多线程)
  • is_alive()如果该线程还没运行完,就是True否则False
  • daemon 返回该线程的daemon
  • setDaemon(True)设置线程的daemon

(4)threading

一些直接调用的变量

  • threading.currentThread(): 返回当前的线程变量
  • threading.enumerate(): 返回一个包含正在运行的线程的list
  • threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果

参考

Python Web学习笔记之Python多线程基础的更多相关文章

  1. Python Web学习笔记之Python多线程和多进程、协程入门

    进程和线程究竟是什么?如何使用进程和线程?什么场景下需要使用进程和线程?协程又是什么?协程和线程的关系和区别有哪些? 程序切换-CPU时间的分配 首先,我们的任何一个程序都需要运行在一个操作系统中,如 ...

  2. Python+Selenium学习笔记5 - python官网的tutorial - 交互模式下的操作

    这篇笔记主要是从Python官网的Tutorial上截取下来,再加上个人理解 1. 在交互模式下,下划线'_'还可以表示上一步的计算结果 2.引号转义问题. 从下图总结的规律是,字符串里的引号如果和引 ...

  3. Python Web学习笔记之多线程编程

    本次给大家介绍Python的多线程编程,标题如下: Python多线程简介 Python多线程之threading模块 Python多线程之Lock线程锁 Python多线程之Python的GIL锁 ...

  4. Python Web学习笔记之GIL机制下的鸡肋多线程

    为什么有人会说 Python 多线程是鸡肋?知乎上有人提出这样一个问题,在我们常识中,多进程.多线程都是通过并发的方式充分利用硬件资源提高程序的运行效率,怎么在 Python 中反而成了鸡肋? 有同学 ...

  5. Python Web学习笔记之TCP/IP、Http、Socket的区别

    经常在笔试.面试或者工作的时候听到这些协议,虽然以前没怎么涉及过,但至少知道这些是和网络编程密不可分的知识,作为一个客户端开发程序员,如果可以懂得网络编程的话,他的作用和能力肯定会提升一个档次.原因很 ...

  6. Python Web学习笔记之为什么设计GIL

    GIL(global interpreter lock),全局解释器锁,是很多编程语言实现中都具有的特性,由于它的存在,解释器无法实现真正的并发.它也是 Python 中经常讨论的话题之一. Pyth ...

  7. Python Web学习笔记之WebSocket原理说明

    众所周知,Web应用的通信过程通常是客户端通过浏览器发出一个请求,服务器端接收请求后进行处理并返回结果给客户端,客户端浏览器将信息呈现.这种机制对于信息变化不是特别频繁的应用可以良好支撑,但对于实时要 ...

  8. Python Web学习笔记之并发编程IO模型

    了解新知识之前需要知道的一些知识 同步(synchronous):一个进程在执行某个任务时,另外一个进程必须等待其执行完毕,才能继续执行 #所谓同步,就是在发出一个功能调用时,在没有得到结果之前,该调 ...

  9. Python Web学习笔记之socket编程

    Python 提供了两个基本的 socket 模块. 第一个是 Socket,它提供了标准的 BSD Sockets API. 第二个是 SocketServer, 它提供了服务器中心类,可以简化网络 ...

随机推荐

  1. TFS二次开发09——查看文件历史(QueryHistory)

    这篇文章给大家展示怎样获取一个文件的历史版本,内容很简单,直接上代码了. string tpcURL = "http://127.0.0.1:8080/"; TfsTeamProj ...

  2. rs.getMetadata

    元数据(MetaData),即定义数据的数据.打个比方,就好像我们要想搜索一首歌(歌本身是数据),而我们可以通过歌名,作者,专辑等信息来搜索,那么这些歌名,作者,专辑等等就是这首歌的元数据.因此数据库 ...

  3. HDFS 命令深入浅出

    HDFS 命令深入浅出~ [root@neusoft-master ~]# hadoop dfs Usage: hadoop fs [generic options] [-appendToFile & ...

  4. ZOJ 3781 - Paint the Grid Reloaded - [DFS连通块缩点建图+BFS求深度][第11届浙江省赛F题]

    题目链接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=3781 Time Limit: 2 Seconds      Me ...

  5. python读取文件行号和内容的便捷方法

    处理数据时候,需要得到数据所在和行号,使用enumerate时便捷的方法: file = open('file.txt','r') for (num,value) in enumerate(file) ...

  6. CNN中的池化层的理解和实例

    池化操作是利用一个矩阵窗口在输入张量上进行扫描,并且每个窗口中的值通过取最大.取平均或其它的一些操作来减少元素个数.池化窗口由ksize来指定,根据strides的长度来决定移动步长.如果stride ...

  7. Python yield 使用浅析(转)

    add by zhj: 说到yield,就要说说迭代器.生成器.生成器函数. 迭代器:其实就是一个可迭代对象,书上说迭代器,我个人不喜欢这个说法,有点晦涩.可迭代对象基本上可以认为是有__iter__ ...

  8. ansible-playbook剧本

    Playbooks 是一种简单的配置管理系统与多机器部署系统的基础, 非常适合于复杂应用的部署 由 yaml 语言编写, 运行过程中, ansible-playbook 命令根据自上而下的顺序依次执行 ...

  9. Input的类型(type)

    HTML5 新的 Input 类型 HTML5 拥有多个新的表单输入类型.这些新特性提供了更好的输入控制和验证. 本章全面介绍这些新的输入类型: color date datetime datetim ...

  10. [js]js中原型的继承

    js继承01 思路: 单例/工厂/构造函数--演进到原型 搞清原型结构 原型继承 模拟系统原型继承 实现自己的继承 观察原型继承特点 演进到原型链这一步 //单例模式: 防止变量名冲突: // 思路: ...