Python多线程编程详解,文章比较长,需耐心浏览
前言

看过金庸武侠小说《射雕英雄传》和《神雕侠侣》的单鞋们应该都知道里面有一个人物叫老顽童周伯通,他会一种“双手互搏”的武功,可以同时左手画圆右手画方,一心二用,两只手同时施展不同的武功,后来小龙女也学会了这种功夫,一个人就可以施展本来需要两个人才可以施展的“玉女素心剑法”,功力大幅提升。
文末领取读者福利
在编程中,也有一种方法可以在一个程序运行时,同时做不同的任务,我们将这种方法叫做“多线程”。


多线程(英语:multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。
在Python编程中,内置了threading工具包,提供了Thread类,我们可以使用它来进行多线程编程。
创建多线程
#引入线程类。
from threading import Thread
预设线程要做的“任务”,一般是一个自定义函数,下面是2个自定义函数,task1需要5秒(我们使用了time工具包的sleep()函数让它等待5秒)做完;task2有一个参数n,它会重复n次从1打印到n。
def task1():
print("任务1开始")
sleep(5)
print("任务1结束")
def task2(n):
print("任务2开始")
for i in range(n):
print(i+1)
print("任务2完成")
创建线程对象,让不同的线程去执行不同的“任务”。
# 线程t1去做任务1
t1 = Thread(target=task1)
# 线程t2去做任务2,任务参数是5
t2 = Thread(target=task2, args=[5,])
开始让线程们工作
t1.start()
t2.start()
完整代码
from threading import Thread
from time import sleep
# 任务1
def task1():
print("任务1开始")
sleep(5)
print("任务1结束")
# 任务2
def task2(n):
print("任务2开始")
for i in range(n):
print(i+1)
print("任务2完成")
# 线程t1去做任务1
t1 = Thread(target=task1)
# 线程t2去做任务2,任务参数是5
t2 = Thread(target=task2, args=[5,])
t1.start()
t2.start()
print("主线程结束")
运行结果如下:
任务1开始
任务2开始
1
2
3
4
主线程结束
5
任务2完成
任务1结束
主线程等待其他线程完成再结束

在上面的例子中,我们可以看到主线程不等线程1和线程2完成,它就完成任务了,有时我们需要主线程等待子线程完成任务之后再结束,这时我们可以对线程对象使用join()方法。

from threading import Thread
from time import sleep
......
t1.start()
t2.start()
# 主线程等待t1和t2完成任务之后再结束
t1.join()
t2.join()
print("主线程结束")
运行结果如下:
任务1开始
任务2开始
1
2
3
4
5
任务2完成
任务1结束
主线程结束
守护线程
在上面的例子中,进程t1和t2可以在主线程结束之后继续工作,除非我们使用join()方法,让主线程等待他们完成任务之后再结束。我们还可以创建一种“守护线程”,“守护线程”可以理解为主线程创建他们之后,如果主线程结束了,不管子线程有没有完成任务,他们都会被停止工作,

from threading import Thread
from time import sleep
# 任务1
def task1():
print("任务1开始")
sleep(5)
print("任务1结束")
# 任务2
def task2(n):
print("任务2开始")
for i in range(n):
sleep(1)
print(i+1)
print("任务2完成")
# 线程t1去做任务1
t1 = Thread(target=task1)
# 线程t2去做任务2,任务参数是5
t2 = Thread(target=task2, args=[5,], daemon=True)
t1.start()
t2.start()
print("主线程结束")
运行结果如下,我们可以看到主线程结束后,线程2的任务2没有完成也被停止了
任务1开始
任务2开始
主线程结束
1
2
3
4
任务1结束
使用daemon=True的线程t2被称之为“守护线程”, 主线程不用担心它,当主线程结束时,它会自动停止。
线程实例
我们经常在程序中使用的任务队列可以使用线程来实现,线程1负责往任务队列里增加任务,线程2(或者是多个线程)负责从队列中取出任务执行,现在我们来实用Python编程来制作一个简单的任务队列吧。
from threading import Thread
from time import sleep
# 一个任务列表
task_list = []
# 增加任务,将由线程1来执行
def add_task():
while True:
task = input()
task_list.append(task)
if task == "完成":
# 如果用户输入的是完成,则break这个循环,不再执行
print("增加任务完成")
break
# 执行任务,将由线程2来执行
def pop_task():
while True:
if task_list:
task = task_list.pop()
if task == "完成":
# 如果任务内容是“完成”,则break这个循环,不再执行
print("执行任务完成")
break
print("执行:{}".format(task))
else:
sleep(0.1)
# 创建2个线程
t1 = Thread(target=add_task)
t2 = Thread(target=pop_task)
# 让2个线程开始工作
t1.start()
t2.start()
# 主线程等待线程1和线程2都完成才结束
t1.join()
t2.join()
# 主线程打印完成
print("主线程完成")
运行结果如下:
aaa
执行:aaa
bbb
执行:bbb
ccc
执行:ccc
ddd
执行:ddd
qwer
执行:qwer
qewr
执行:qewr
qwr
q执行:qwr
er
q执行:qer
er
执行:qer
完成
增加任务完成
执行任务完成
主线程完成
在实际应用中,往往将一些比较耗时的工作放进任务队列中,由单独的线程去做,比如说抓取网页,一个线程负责将待抓取网页内容的网址放进任务队列中,另一个(或者多个)线程负责从任务队列中取出网址去执行抓取任务;或者控制机器人,机器人的传感器线程将获取的数据存放进任务队列,另一个(或者多个)线程负责从任务队列中取出数据,执行相应的控制机器人的动作等。

浙公网安备 33010602011771号