多线程理解

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

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. hdu4614 Vases and Flowers【线段树】【二分】

    Alice is so popular that she can receive many flowers everyday. She has N vases numbered from 0 to N ...

  2. HDU 2829 - Lawrence - [斜率DP]

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2829 T. E. Lawrence was a controversial figure during ...

  3. ubuntu环境下快速搭建开发环境

    接触ubuntu已经半年了,虽然游戏啊qq啊在linux下配置稍微麻烦一些,但是作为开发环境,ubuntu真的是好东西,无论是c啊还是php and etc 看到官网上文档开发环境建议wamp,如果是 ...

  4. 为什么使用 Redis及其产品定位(转)

    原文:http://www.infoq.com/cn/articles/tq-why-choose-redis 传统MySQL+ Memcached架构遇到的问题 实际MySQL是适合进行海量数据存储 ...

  5. Eclipse+Pydev 搭建开发环境(转)

    add by zhj: 对原文略有修改 原文:http://www.cnblogs.com/TankXiao/archive/2013/05/29/3033640.html C#之所以容易让人感兴趣, ...

  6. redis 介绍

    缓存数据库介绍 NoSQL(NoSQL = Not Only SQL ),意即“不仅仅是SQL”,泛指非关系型的数据库,随着互联网web2.0网站的兴起,传统的关系数据库在应付web2.0网站,特别是 ...

  7. 虚拟机VMware的网络设置出了问题会导致很多莫名的错误

    邪门地CentOS内软件安装失败问题:Xshell与虚拟机的各种连接失败:CentOS下eth0没显示ip地址. 这些原因竟然是一个,虚拟机VMware的网络设置出了问题.     恢复初始设置即可.

  8. SparkSQL UDF两种注册方式:udf() 和 register()

    调用sqlContext.udf.register() 此时注册的方法 只能在sql()中可见,对DataFrame API不可见 用法:sqlContext.udf.register("m ...

  9. [py][mx]django课程页显示city和机构封面图

    city和课程机构信息展示到前台去 organization/views.py from django.views.generic.base import View from organization ...

  10. WebService之Axis2(2):复合类型数据的传递

    在实际的应用中,不仅需要使用WebService来传递简单类型的数据,有时也需要传递更复杂的数据,这些数据可以被称为复合类型的数据.数组与类(接口)是比较常用的复合类型.在Axis2中可以直接使用将W ...