python多线程——threading模块

守护线程

threading模块支持守护线程,其工作方式是:守护线程一般是一个等待客户端请求服务的服务器。如果没有客户端请求,守护线程就是空闲。如果把一个线程设置为守护线程,就表示这个线程是不重要的,进程退出时不需要等待这个线程执行完成。
设置线程为守护线程:

thread.daemon = True

检查线程的守护状态,检查这个值即可。

整个python程序将在所有非守护线程退出之后才退出,也就是守护线程是保证其他非守护线程正常运行。

创建线程

使用Thread创建线程的方法(推荐选择第一三个):

  • 创建Thread的实例,传给它一个函数。
  • 创建Thread的实例,传给它一个可调用的类实例。
  • 派生Thread的子类,并创建子类的实例。

创建Thread的实例,传给它一个函数

代码

import threading
from time import sleep,ctime

loops = [4,2]

def loop(nloop,nsec):
    print('start loop ',nloop,' at:',ctime())
    sleep(nsec)
    print('loop ',nloop,' done at:',ctime())

def main():
    print('starting at:',ctime())
    threads = []
    nloops = range(len(loops))

    for i in nloops:
        t = threading.Thread(target=loop,args=(i,loops[i])) # 实例化Thread
        # loop为目标函数名,args为目标函数参数
        threads.append(t)

    for i in nloops:    # 开始线程
        threads[i].start()

    for i in nloops:    # 等待所有
        threads[i].join()   # 线程完成

    print('all DONE at:',ctime())

if __name__ == "__main__":
    main()

输出

starting at: Thu Sep 10 16:18:48 2020
start loop  0  at: Thu Sep 10 16:18:48 2020
start loop  1  at: Thu Sep 10 16:18:48 2020
loop  1  done at: Thu Sep 10 16:18:50 2020
loop  0  done at: Thu Sep 10 16:18:52 2020
all DONE at: Thu Sep 10 16:18:52 2020

join()方法将等待线程结束,或者在提供了超时时间的情况下,达到超时时间。使用join()方法要比等待锁释放的无限循环更加清晰(这种锁又称为自旋锁的原因)。如果主线程还有其他事情要去做,而不是等待这些线程完成(例如其他处理或者等待新的客户端请求),就可以不调用join()。join()方法只有在你需要等待线程完成的时候才是有用的。

创建Thread的实例,传给它一个可调用的类实例

代码

import threading
from time import sleep,ctime

loops = [4,2]

class ThreadFunc(object):

    def __init__(self,func,args,name=''):
        self.name = name
        self.func = func
        self.args = args

    def __call__(self):
        self.func(*self.args)

def loop(nloop,nsec):
    print('start loop ',nloop,' at:',ctime())
    sleep(nsec)
    print('loop ',nloop,' done at:',ctime())

def main():
    print('starting at:',ctime())
    threads = []
    nloops = range(len(loops))

    for i in nloops:    # 创建所有线程
        t = threading.Thread(
            target=ThreadFunc(loop,(i,loops[i]),
            loop.__name__))
        threads.append(t)

    for i in nloops:    # 开始所有线程
        threads[i].start()

    for i in nloops:    # 等待完成
        threads[i].join()

    print('all DONE at:',ctime())

if __name__ == "__main__":
    main()

输出

starting at: Thu Sep 10 16:43:57 2020
start loop  0  at: Thu Sep 10 16:43:57 2020
start loop  1  at: Thu Sep 10 16:43:57 2020
loop  1  done at: Thu Sep 10 16:43:59 2020
loop  0  done at: Thu Sep 10 16:44:01 2020
all DONE at: Thu Sep 10 16:44:01 2020

在实例化Thread对象时同时实例化可调用类ThreadFunc,同时这个类保存了函数的参数args、函数自身func以及函数名的字符串name。当创建新线程时,Thread类的代码将调用ThreadFunc对象,此时会调用__call__()这个方法(该方法的功能类似于在类中重载 () 运算符,使得类实例对象可以像调用普通函数那样,以"对象名()"的形式使用)。`由于我们已经有了要用到的参数,这里就不需要再将其传递给Thread()的构造函数了,直接调用即可。

派生Thread的子类,并创建子类的实例

代码

import threading
from time import sleep,ctime

loops = (4,2)

class MyThread(threading.Thread):

    def __init__(self,func,args,name=''):
        threading.Thread.__init__(self)
        self.name = name
        self.func = func
        self.args = args

    def run(self):
        self.func(*self.args)

def loop(nloop,nsec):
    print('start loop ',nloop,' at:',ctime())
    sleep(nsec)
    print('loop ',nloop,' done at:',ctime())

def main():
    print('starting at:',ctime())
    threads = []
    nloops = range(len(loops))

    for i in nloops:
        t = MyThread(loop,(i,loops[i]),loop.__name__)
        threads.append(t)

    for i in nloops:
        threads[i].start()

    for i in nloops:
        threads[i].join()

    print('all DONE at:',ctime())

if __name__ == '__main__':
    main()

输出

starting at: Thu Sep 10 17:18:35 2020
start loop  0  at: Thu Sep 10 17:18:35 2020
start loop  1  at: Thu Sep 10 17:18:35 2020
loop  1  done at: Thu Sep 10 17:18:37 2020
loop  0  done at: Thu Sep 10 17:18:39 2020
all DONE at: Thu Sep 10 17:18:39 2020

相比创建Thread的实例,传给它一个可调用的类实例,派生子类有两点变化:

  1. MyThread子类的构造函数必须先调用其基类的构造函数;
  2. 之前的特殊方法__call__()在这个子类中必须要写为run()

猜你喜欢

转载自blog.csdn.net/XZ2585458279/article/details/108516586