更好的阅读体验:点这里 ( www.foooor.com
)
11 多线程
11.1 多线程简介
1 进程与线程
在计算机中,进程是指正在执行中的一个程序,它由程序、数据和进程控制块组成。每个进程都有独立的地址空间,相互之间不能直接访问,是操作系统对程序运行进行管理的单位,每个进程都可以拥有多个线程。
线程是指一条执行路径,它是进程中的一部分,可以与其他线程共享进程的资源和内存。线程是轻量级的进程,它比进程更快速,更容易创建和销毁。
2 并行与并发
并行
并行就是两个任务同时运行,就是A任务执行的同时,B任务也在进行,这是需要多核CPU支持的,A任务和B任务由不同的核来执行。
并发
并发是指两个任务都请求运行,而处理器只能接受一个任务,就把两个任务安排轮流进行,由于时间间隔较短,使人感觉两个任务都在运行。以前计算机是单核的时候,就是并发执行,轮流指定多个任务,但是切换任务的速度很快,以为是同时执行多个任务。
11.2 创建多线程
我们之前编写的代码都是单线程的,程序自上而下一行一行执行。
如果我们要实现一边洗澡一边唱歌的功能,应该怎么实现呢?
如果没有多线程,我们可能会这么写:
import time
def bath():
print("我在洗澡...")
time.sleep(1) # sleep(1) 是为了让程序执行的慢一些
def sing():
print("我在唱歌...")
time.sleep(1)
if __name__ == '__main__':
while True:
bath()
sing()
sleep(1)
是为了让程序执行的慢一些,上面的洗澡和唱歌其实并不是同时执行的,而是交替执行的。
1 传递函数创建多线程
在Python中实现多线程功能,需要使用 threading 模块。
开启一个线程主要有两个步骤:
- 创建一个线程
- 调用线程的start()方法,启动这个线程
语法如下:
# 1. 引入线程模块
import threading
# 2. 创建线程
thread = threading.Thread([group], target, [args], [kwargs], [name])
# 3. 启动现场
thread.start()
创建线程的参数:
- group:暂时无用,未来功能的预留参数
- target:要通过新线程执行的函数或方法的名称
- args:以元组的方式给任务传参
- kwargs:以字段的方式给任务传参
- name:线程名,一般不用设置。
举个栗子:
开启两条线程,一个线程洗澡,一个线程唱歌。
import time
import threading
def bath():
while True:
print("我在洗澡...")
time.sleep(1)
def sing():
while True:
print("我在唱歌...")
time.sleep(1)
if __name__ == '__main__':
bath_thread = threading.Thread(target=bath) # 创建一个线程,执行洗澡的函数
sing_thread = threading.Thread(target=sing) # 创建一个线程,执行唱歌的函数
bath_thread.start() # 启动洗澡的线程
sing_thread.start() # 启动唱歌的线程
执行结果:
我在洗澡…
我在唱歌…
我在唱歌…
我在洗澡……
当然我们也可以将函数封装到类中,让线程执行类中的方法:
import time
import threading
class Student:
def bath(self):
while True:
print(f"我在洗澡...")
time.sleep(1)
def sing(self):
while True:
print(f"我在唱歌...")
time.sleep(1)
if __name__ == '__main__':
stu = Student()
bath_thread = threading.Thread(target=stu.bath) # 创建一个线程,执行洗澡的方法
sing_thread = threading.Thread(target=stu.sing) # 创建一个线程,执行唱歌的方法
bath_thread.start() # 启动洗澡的线程
sing_thread.start() # 启动唱歌的线程
2 传递参数
我们在创建线程的时候,可以通过元组或字典传递参数。
举个栗子:
import time
import threading
class Student:
def bath(self, msg, minute):
while True:
print(f"我在洗澡, {msg}, 洗{minute}分钟")
time.sleep(1)
def sing(self, singer, song):
while True:
print(f"我在唱{singer}的{song}")
time.sleep(1)
if __name__ == '__main__':
stu = Student()
# 创建一个线程,通过元组传参
bath_thread = threading.Thread(target=stu.bath, args=("热水", 10))
# 创建一个线程,通过字典传参
sing_thread = threading.Thread(target=stu.sing, kwargs={"singer":"著名歌手", "song":"鸡你太美"})
bath_thread.start() # 启动洗澡的线程
sing_thread.start() # 启动唱歌的线程
3 继承 threading.Thread 创建线程
可以通过继承 threading.Thread
类来创建新的线程:
举个栗子:
import time
import threading
class SingThread(threading.Thread): # 创建了一个线程类,继承threading.Thread
def __init__(self, singer, song):
super().__init__() # 一定要现调用父类的初始化方法
self.singer = singer
self.song = song
def run(self):
while True:
print(f"我在唱{self.singer}的{self.song}")
time.sleep(1)
if __name__ == "__main__":
# 创建了两个新的线程
sing_thread1 = SingThread("ikun", "鸡你太美")
sing_thread2 = SingThread("刘秉义", "我为祖国献石油")
# 启动线程
sing_thread1.start()
sing_thread2.start()
上面创建了线程类,通过这个线程类创建了2个线程,并通过start启动线程。
11.3 线程池
线程池是一种多线程处理方式,它是在程序启动时就创建了一定数量的线程并将它们放入一个线程池中。在需要进行并发处理的时候,程序将任务提交到线程池中,线程池中的线程将并发地执行这些任务,从而提高了程序的效率和并发处理能力。
线程池可以避免频繁地创建和销毁线程所带来的开销,也可以控制并发线程的数量,避免过度消耗系统资源。
举个栗子:
import time
import concurrent.futures
def sing(singer, song):
while True:
print(f"我在唱{singer}的{song}")
time.sleep(1)
if __name__ == '__main__':
# 创建线程池,线程池中的线程数量为3
executor = concurrent.futures.ThreadPoolExecutor(max_workers=3)
# 提交任务到线程池
executor.submit(sing, "ikun", "鸡你太美")
# 关闭线程池
executor.shutdown()
调用 shutdown()
方法会等待所有已经提交的任务都完成后才会关闭线程池。如果需要强制终止正在执行的任务,可以使用 shutdown(wait=False)
方法,它会立即关闭线程池并强制终止所有正在执行的任务。
也可以将线程类提交到线程池执行:
import threading
import time
import concurrent.futures
class SingThread(threading.Thread):
def __init__(self, singer, song):
super().__init__()
self.singer = singer
self.song = song
def run(self):
while True:
print(f"我在唱{self.singer}的{self.song}")
time.sleep(1)
if __name__ == '__main__':
# 创建线程池,线程池中的线程数量为3
executor = concurrent.futures.ThreadPoolExecutor(max_workers=3)
# 提交任务到线程池
executor.submit(SingThread("ikun", "鸡你太美").start)
# 关闭线程池
executor.shutdown()
也可以使with
语法来创建线程池:
# 使用 with 语句创建线程池
with concurrent.futures.ThreadPoolExecutor(max_workers=3) as executor:
executor.submit(SingThread("ikun", "鸡你太美").start)
在 with
语句块中使用了线程池,当当线程池中的线程执行完毕时,with
语句块会自动关闭线程池,无需手动调用 shutdown()
方法。