python多线程基础

本文首发于知乎python

多线程理解编程

多线程是多个任务同时运行的一种方式。好比一个循环中,每一个循环看作一个任务,咱们但愿第一次循环运行还没结束时,就能够开始第二次循环,用这种方式来节省时间。多线程

python中这种同时运行的目的是最大化利用CPU的计算能力,将不少等待时间利用起来。这也说明若是程序耗时不是由于等待时间,而是任务很是多,就是要计算那么久,则多线程没法改善运行时间。app

更多有关多线程理解的内容能够参考下面资料函数

简单使用ui

先看下面这个函数lua

import time
def myfun():
time.sleep(1)
a = 1 + 1
print(a)
复制代码

若是咱们要运行10次这个函数,它的运行时间主要在于每次sleep的那一秒,1 + 1的计算是不会耗多少时间的。这种状况能够用多线程提升效率。spa

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

不使用多线程code

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编程

专栏目录:目录

版本说明:软件及包版本说明

相关文章
相关标签/搜索