文章目录
一、前言
使用threading库,咱们的python程序就能从单线程串行变成多线程并发。python的threading库实现的“并发”是假的并发,即同一时刻只有一个线程在运行。据说python后来想将这个假的“并发”改为真的并发,但出现特别多的BUG,最后放弃了这个想法。
聊到假的“并行”,学习过单片机(STM32)的嵌入式实时系统RTOS(ucosIII、freeRTOS、RTX5等)的同学就知道,移植了RTOS的单片机,同一时刻仅仅只有一个线程在运行。只是RTOS在很快的切换线程,产生了好像多个线程在同时工作的“假象”。
python多线程与单片机的RTOS在调度规则完全不一样。python多线程的调度程序会在任何时候中断线程(相当于调度,所以python多线程的调度可以说是很难控制的),单片机的RTOS会有相应的API来产生调度(调度是可控的)。
线程与协程之间的比较还有最后一点要说明:如果使用线程做过重要的编程,就知道写出程序有多么困难,因为调度程序任何时候都可能中断线程。必须记住保留锁,去保护程序中的重要部分,防止多步操作在执行的过程中被中断,防止数据处于无效状态。 —《流畅的Python》- Luciano Ramalho
所以,在我看来:python的协程与单片机的RTOS才是相似的。
二、创建两个同时运行的线程
2.1、代码
# python3.9
import time
import threading
def thread1():
"""
线程1
"""
while True:
# 打印信息,并打印性能计数器的值
print("I am thread1,time is %f" % time.perf_counter())
time.sleep(1) # 休眠1秒
def thread2():
"""
线程2
"""
while True:
# 打印信息,并打印性能计数器的值
print("I am thread2,time is %f" % time.perf_counter())
time.sleep(1) # 休眠1秒
def main():
"""
主线程
"""
t1 = threading.Thread(target=thread1) # 创建thread1线程
t2 = threading.Thread(target=thread2) # 创建thread2线程
t1.start() # 启动thread1线程
t2.start() # 启动thread2线程
if __name__ == "__main__":
main()
2.2、运行
从下图可以看到,线程1与线程2都是每隔约1秒时间运行一次。线程1与线程2哪个先运行取决于系统的调度。
三、threading库的函数
3.1、threading.Thread()
入口参数:
- group - 暂时不使用
- target - 用于执行线程需要执行的线程函数
- name - 可以自定义线程的名字,不设置的话,系统就自动分配
- args - 创建线程时,可以给线程函数传递参数
- kwargs - 暂时不使用
- daemon - 设置线程是否为守护线程(后台线程)
当我们不对入口参数name赋值的话,系统会自动分配线程名字。
运行代码:
接着,我尝试对入口参数name进行赋值。
运行代码:
另外一个重要的入口参数是daemon,它的作用是设置线程是否为守护线程(后台线程)。守护线程有一个重要的特点是主线程(例子的main()线程)退出时,守护线程不管怎样都要跟主线程一起退出。
守护线程的目的与作用:
所以,我的理解是:当创建的子线程是无限循环的话,就应该设置为守护线程。随着主线程的退出,子线程被强制退出,保证了整个python程序正常地,完整地退出。
运行结果:
main()线程退出,线程t1与t2也跟着退出了。有什么办法解决这个问题?
3.2、threading.join()
join()方法可以让主线程进入阻塞态,等待其他线程运行结束,接着才解除阻塞态。
3.3、threading.start()
start()方法用于启动线程的运行,否则线程不会跑起来。
四、最终的完整代码
- 使用守护线程,主线程退出时,强制让所有的子线程退出,确保整个python程序正常地退出。
- 使用threading.join()方法,让主线程等子线程结束,无限循环的子线程可以持续无限执行。
# python3.9
import time
import threading
def thread1():
"""
线程1
"""
while True:
# 打印信息,并打印性能计数器的值
print("I am thread1,time is %f" % time.perf_counter())
time.sleep(1) # 休眠1秒
def thread2():
"""
线程2
"""
while True:
# 打印信息,并打印性能计数器的值
print("I am thread2,time is %f" % time.perf_counter())
time.sleep(1) # 休眠1秒
def main():
"""
主线程
"""
t1 = threading.Thread(target=thread1,name="fun_thread1",daemon=True) # 创建thread1线程
t2 = threading.Thread(target=thread2,name="fun_thread2",daemon=True) # 创建thread2线程
t1.start() # 启动thread1线程
t2.start() # 启动thread2线程
print("t1的线程名字是 %s" % t1.getName()) # 打印t1线程的线程名字
print("t2的线程名字是 %s" % t2.getName()) # 打印t2线程的线程名字
t1.join() # 当前需要等待线程t1执行完毕后才能运行下一步
t2.join() # 当前需要等待线程t2执行完毕后才能运行下一步
print("主线程执行完毕!")
if __name__ == "__main__":
main()