Python编程基础:第五十八节 线程Threading

第五十八节 线程Threading

前言

线程就是一个独立的代码执行流程,在一个线程内部Python会按照先后顺序执行指定的代码流。这里我们思考一下,如果我们创建多个线程,并为每个线程指定不同的任务不就可以实现并发操作了吗?这种设计方式就像工厂上班一样,我可以雇佣很多人然后把他们分为多个小组例如A、B、C,其中A组去制作产品的控制板、B组去制作产品的壳子、C组负责装配。我们同时调度三组员工并行执行任务一定比调度一组员工先做控制板,再做壳子,最后装配来得快。与实际情况不同的是,Python的GIL(global interpreter lock)设计使得在任何时候都只允许一个线程掌握Python解释器的控制权,那么我们只有通过协调每个线程轮流运行以实现并发性。
CPU bound:程序/任务大部分时间都在等待内部事件(计算密集型),使用多进程(multiprocessing)
IO bound:程序/任务大部分时间都在等待外部事件(用户输入,网页抓取),使用多线程(multithreading)
一般而言,多线程比多进程更加有效

实践

为了使用多线程,我们首先需要导入模块

import threading

现在我们思考这样一个场景,你在早上上学前需要做三件事情,分别是吃早餐、喝咖啡、以及补作业,其中吃早餐需要3秒,喝咖啡需要4秒,补作业需要5秒,我们写函数来实现这三个任务:

import time

def eat_breakfast():
    time.sleep(3)
    print("eat breakfast finish")
    
def drink_coffee():
    time.sleep(4)
    print("drink coffee finish")
    
def study():
    time.sleep(5)
    print("study finish")

如果不使用多线程,那我们只能按照顺序执行三个任务

start = time.time()
eat_breakfast()
drink_coffee()
study()
end = time.time()
print('总用时: {} 秒'.format(end-start))
>>> eat breakfast finish
>>> drink coffee finish
>>> study finish
>>> 总用时: 12.00914740562439

不难发现程序按照顺序执行三个函数,总用时时间大约等于各个函数执行时间之和,那我们如何使用多线程来并行化这三项任务呢,代码如下

start = time.time()
x = threading.Thread(target=eat_breakfast, args=())
x.start()
y = threading.Thread(target=drink_coffee, args=())
y.start()
z = threading.Thread(target=study, args=())
z.start()
end = time.time()
print('总用时: {} 秒'.format(end-start))
>>> 总用时: 0.0018167495727539062>>> eat breakfast finish
>>> drink coffee finish
>>> study finish

这里会发现一个奇怪的现象,先打印出代码的执行时间,然后打印各个函数的输出,貌似是首先执行了print()语句,然后执行三个函数。这是因为time模块是给主线程计时,而主线程的任务就是创建三个子线程,所以它能够在极短的时间内运行完,而各个子线程各自运行完分别打印其输出即可。这里可以想象成一棵树,树干是主线程,树枝是三个子线程,他们之间的生长速度是相互独立的,树干(主线程)停止生长不影响树枝(子线程)的生长速度。那是否有一种方式使主线程等待所有子线程执行完呢,其实是可以的:

start = time.time()
x = threading.Thread(target=eat_breakfast, args=())
x.start()
y = threading.Thread(target=drink_coffee, args=())
y.start()
z = threading.Thread(target=study, args=())
z.start()
x.join()
y.join()
z.join()
end = time.time()
print('总用时: {} 秒'.format(end-start))
>>> eat breakfast finish
>>> drink coffee finish
>>> study finish
>>> 总用时: 5.015807390213013

通过join()函数便可使主线程等待子线程结束后再结束,此时主线程的执行时间几乎与最慢子线程执行时间相同。当然我们还可以查看当前的线程数目以及各个线程的名称:

x = threading.Thread(target=eat_breakfast, args=())
x.start()
y = threading.Thread(target=drink_coffee, args=())
y.start()
z = threading.Thread(target=study, args=())
z.start()
print(threading.active_count()) # 打印线程个数
print(threading.enumerate())    # 打印线程名称
print(time.perf_counter())      # 打印主线程执行时间
>>> 4
>>> [<_MainThread(MainThread, started 18896)>, <Thread(Thread-1, started 15528)>, <Thread(Thread-2, started 5748)>, <Thread(Thread-3, started 7500)>]
>>> 0.7035468
>>> eat breakfast finish
>>> drink coffee finish
>>> study finish

本节完整代码如下:

import threading
import time


def eat_breakfast():
    time.sleep(3)
    print("eat breakfast finish")


def drink_coffee():
    time.sleep(4)
    print("drink coffee finish")


def study():
    time.sleep(5)
    print("study finish")


x = threading.Thread(target=eat_breakfast, args=())
x.start()
y = threading.Thread(target=drink_coffee, args=())
y.start()
z = threading.Thread(target=study, args=())
z.start()

print(threading.active_count())  # 打印线程个数
print(threading.enumerate())     # 打印线程名称

x.join()
y.join()
z.join()

print(time.perf_counter())       # 打印主线程执行时间
>>> 4
>>> [<_MainThread(MainThread, started 7264)>, <Thread(Thread-1, started 8356)>, <Thread(Thread-2, started 18672)>, <Thread(Thread-3, started 14300)>]
>>> eat breakfast finish
>>> drink coffee finish
>>> study finish
>>> 5.4419603
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值