zoukankan      html  css  js  c++  java
  • Python Web学习笔记之Python多线程基础

    多线程理解

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

    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())有相同的结果

    参考

  • 相关阅读:
    basic-linux
    巧用border属性
    git常用操作笔记
    如何删除github里的项目
    常用css3属性的ie兼容查看
    新建pc端页面的模板
    HTML5 Shiv--解决IE(IE6/IE7/IE8)不兼容HTML5标签的方法
    进程和线程
    C++对象模型---第 4 章 Function语意学
    C++对象模型---第 3 章 Data语意学
  • 原文地址:https://www.cnblogs.com/JetpropelledSnake/p/8931194.html
Copyright © 2011-2022 走看看