多线程(一)

Python中提供了多个模块来支持多线程编程,包括thread,threading,Queue模块名等。thread提供了基本的线程和锁定支持;threading模块提供了更高级别、功能更全面的线程管理;Queue模块提供了一个数据结构,使得数据可以在多线程之间共享

thread(python3中为_thread)

低级模块,不推荐使用

thread模块相对来说比较低级,除了派生线程,还提供了基本的同步数据结构(lock object)。

下面是一个例子

import _thread
from time import sleep, ctime

def loop0():
    print(f'start loop 0 at {ctime()}')
    sleep(4)
    print(f'loop 0 done at {ctime()} ')

def loop1():
    print(f'start loop 1 at {ctime()}')
    sleep(2)
    print(f'loop 1 done at {ctime()} ')

def main():
    print(f'starting at {ctime()}')
    _thread.start_new_thread(loop0,())
    _thread.start_new_thread(loop1,())
    sleep(6)
    print(f'all done at {ctime()}')

if __name__ == "__main__":
    main()

上述例子的运行结果为:

starting at Sun Mar 15 13:32:20 2020
start loop 0 at Sun Mar 15 13:32:20 2020
start loop 1 at Sun Mar 15 13:32:20 2020
loop 1 done at Sun Mar 15 13:32:22 2020
loop 0 done at Sun Mar 15 13:32:24 2020
all done at Sun Mar 15 13:32:26 2020

我们可以看到,两个函数loop0loop1(几乎)在同一时启动,并且在其指定的休眠时间后完成(4s,2s),最终两个函数运行时间约为4s。在这里我们需要注意到的一点是,主线程中有一个休眠6s的语句,这是因为在启动子线程之后,主线程是会继续运行的,而当主线程运行完成后,无论子线程是否运行完毕,程序都会退出。

如果想要避免这种主线程不顾子线程死活的情况,则需要上锁

from time import sleep, ctime
import _thread


def loop(nloop, nsec, lock):
    print(f"start loop {nloop} at {ctime()}")
    sleep(nsec)
    print(f"loop {nloop} done at {ctime()} ")
    lock.release()


def main():
    print(f"starting at {ctime()}")

    loops = [4, 2]
    locks = []
    for i in loops:
        lock = _thread.allocate_lock()
        lock.acquire()
        locks.append(lock)

    for i in range(len(loops)):
        _thread.start_new_thread(loop, (i, loops[i], locks[i]))

    for i in locks:
        while i.locked():
            pass

    print(f"all done at {ctime()}")


if __name__ == "__main__":
    main()

我们针对每个线程都请求了一个锁,当线程结束,释放~当所有子线程结束时,所有的锁都已释放,打印最终完成时间

threading 模块

threading相对于thread模块来说更为高级,他提供了多种可在多线程中使用的类,其中Thread类是我们在多线程编程中索要使用到的主要的类。

Thread类的主要属性有:

属性 描述
name 线程名
ident 线程标识符
daemon 布尔标志,表示这个线程是守护线程

Thread类的主要方法有:

方法 描述
start() 开始执行该线程
run() 定义线程功能的方法
join(timeout=None) 直至启动的线程终止之前一直挂起;除非给出timeout,否则会一直阻塞

使用Thread创建线程主要有三种方法:

  1. 创建Thread的实例,传给他一个函数
  2. 创建Thread的实例,传给他一个可调用的类的实例
  3. 派生Thread的子类,并创建子类的实例

常用的为第一和第三中方法

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

from threading import Thread
from time import sleep, ctime


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


def main():
    print(f'Starting at {ctime()}')
    threads = []
    loops = [4, 2]

    for i in range(len(loops)):
        t = Thread(target=loop, args=(i, loops[i]))
        threads.append(t)

    for i in threads:
        i.start()

    for i in threads:
        i.join()

    print(f'All done at {ctime()}')


if __name__ == "__main__":
    main()

上述例子的运行结果为:

Starting at Sun Mar 15 21:09:31 2020
start loop 0 at Sun Mar 15 21:09:31 2020
start loop 1 at Sun Mar 15 21:09:31 2020
loop 1 done at Sun Mar 15 21:09:33 2020
loop 0 done at Sun Mar 15 21:09:35 2020
All done at Sun Mar 15 21:09:35 2020

他使用了更为简单的逻辑来实现了之前的代码相同的功能,不用我们手动的去对锁进行操作。同时,在子线程完全结束之前,主线程是不会退出的。你可以尝试这把join方法前后的代码注释掉来查看结果

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

from threading import Thread
from time import sleep, ctime


class MyThread(Thread):
    def __init__(self, func, args, name=''):
        super(MyThread, self).__init__()
        self.name = name
        self.func = func
        self.args = args

    def get_result(self):
        return self.res

    def run(self):
        print(f'starting {self.name} at {ctime()}')
        self.res = self.func(*self.args)
        print(f'{self.name} finished at {ctime()}')


def loop(nloop, nsec):
    sleep(nsec)


def main():
    print(f'Starting at {ctime()}')
    threads = []
    loops = [4, 2]

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

    for i in threads:
        i.start()

    for i in threads:
        i.join()

    print(f'All done at {ctime()}')


if __name__ == "__main__":
    main()

这里定义了一个继承自Thread的子类MyThread,并且加入了可以获取执行结果的方法get_result

你可能感兴趣的:(多线程(一))