多线程理解

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

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()表示当前线程,可以调用name或getName()获取线程名称
  • 任何进程都会默认启动一个线程,默认名称为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.Thread和threading.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. HDU-5965 扫雷 模拟+想法

    http://acm.hdu.edu.cn/showproblem.php?pid=5965 (合肥)区域赛签到题...orz 题意:3*n的地图上扫雷(规则就是正常扫雷),中间一排全部没有雷,且全部 ...

  2. YARN Architecture

    The fundamental idea of YARN is to split up the functionalities of resource management and job sched ...

  3. python数据结构之树(二分查找树)

    本篇学习笔记记录二叉查找树的定义以及用python实现数据结构增.删.查的操作. 二叉查找树(Binary Search Tree) 简称BST,又叫二叉排序树(Binary Sort Tree),是 ...

  4. linux:查找搜索文件

    学习内容来源: 实验楼链接: https://www.shiyanlou.com/ 与搜索相关的命令常用的有 whereis,which,find 和 locate . whereis 简单快速 $w ...

  5. android stream media

    http://www.cnblogs.com/skyseraph/archive/2012/03/31/2427593.html http://www.cnblogs.com/lingyunhu/p/ ...

  6. 学习计划 nginx 中 php的配置详解

    本章只看一个刚下载的nginx是如何支持php的 -- location ~ \.php$ { root html; fastcgi_pass 127.0.0.1:9000; fastcgi_inde ...

  7. Python开发【笔记】:pymsyql 插入一条数据同时获取新插数据的自增id的两种方式

    一.通过cursor.lastrowid import pymysql.cursors # Connect to the database connection = pymysql.connect(h ...

  8. IO流(5)判断功能

    判断功能: * public boolean isDirectory():判断是否是目录 * public boolean isFile():判断是否是文件 * public boolean exis ...

  9. 给sql server2005打补丁报错:无法安装Windows Installer MSP文件

    给sql server2005打补丁报错:无法安装Windows Installer MSP文件 在我们安装完SQL2005数据库后,需要安装SP4补丁时,会出错:无法安装Windows Instal ...

  10. css实现固定行

    如果实现单行文本的溢出显示省略号同学们应该都知道用text-overflow:ellipsis属性来,当然还需要加宽度width属来兼容部分浏览. 实现方法: overflow: hidden; te ...