一、前言
使用threading库,咱们的python程序就能从单线程串行变成多线程并发。python的threading库实现的“并发”是假的并发,即同一时刻只有一个线程在运行。据说python后来想将这个假的“并发”改为真的并发,但出现特别多的BUG,最后放弃了这个想法。
聊到假的“并行”,学习过单片机(STM32)的嵌入式实时系统RTOS(ucosIII、freeRTOS、RTX5等)的同学就知道,移植了RTOS的单片机,同一时刻仅仅只有一个线程在运行。只是RTOS在很快的切换线程,产生了好像多个线程在同时工作的“假象”。
二、创建两个同时运行的线程
2.1、代码
import time
import threading
def thread1():
"""
线程1
"""
while True:
print("I am thread1,time is %f" % time.perf_counter())
time.sleep(1)
def thread2():
"""
线程2
"""
while True:
print("I am thread2,time is %f" % time.perf_counter())
time.sleep(1)
def main():
"""
主线程
"""
t1 = threading.Thread(target=thread1)
t2 = threading.Thread(target=thread2)
t1.start()
t2.start()
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()方法,让主线程等子线程结束,无限循环的子线程可以持续无限执行。
import time
import threading
def thread1():
"""
线程1
"""
while True:
print("I am thread1,time is %f" % time.perf_counter())
time.sleep(1)
def thread2():
"""
线程2
"""
while True:
print("I am thread2,time is %f" % time.perf_counter())
time.sleep(1)
def main():
"""
主线程
"""
t1 = threading.Thread(target=thread1,name="fun_thread1",daemon=True)
t2 = threading.Thread(target=thread2,name="fun_thread2",daemon=True)
t1.start()
t2.start()
print("t1的线程名字是 %s" % t1.getName())
print("t2的线程名字是 %s" % t2.getName())
t1.join()
t2.join()
print("主线程执行完毕!")
if __name__ == "__main__":
main()
|