同步任务 我们在此之前编写的代码都是同步代码,代码从上到下按顺序执行,如果前一个任务没有完成,那么不能运行之后的任务 示例 import timedef work_1():print('任务1...')time.sleep(2)def work_2():print('任务2...')time.sleep(2)start = time.time()work_1()work_2()end = time.time()print(f'总共用时: {end - start} s') 任务1...任务2...总共用时: 4.020084857940674 s 可以看到整个程序用时 4 4 4秒,work_2()需要等待work_1()运行结束后才能运行
并发任务 使用线程来运行上面的代码,能够优化运行时间 示例 import timeimport threadingdef work_1():print('任务1...')time.sleep(2)def work_2():print('任务2...')time.sleep(2)# 通过 Thread 类创建线程对象, 并使用 target 绑定线程对象要运行的任务t1 = threading.Thread(target=work_1)t2 = threading.Thread(target=work_2)# 运行线程start = time.time()t1.start()t2.start()t1.join()t2.join()end = time.time()print(f'总共用时: {end - start} s') 任务1...任务2...总共用时: 2.0165793895721436 s
可以看到整个程序用时 2 2 2秒,work_1()和work_2并发运行
下面的示例可以看到CPU调度线程时的“随机性”
import timeimport threadingdef work_1():for i in range(5):print('任务1...')time.sleep(2)def work_2():for i in range(5):print('任务2...')time.sleep(2)t1 = threading.Thread(target=work_1)t2 = threading.Thread(target=work_2)t1.start()t2.start() 任务1...任务2...任务2...任务1...任务1...任务2...任务2...任务1...任务2...任务1... 可以看到任务 1 1 1和任务 2 2 2的调度顺序是我们无法确定的,是由CPU的调度算法决定的线程方法 在学习线程方法之前,我们需要知道Python程序是如何被运行的 一个Python文件被解释器运行时会在操作系统中创建一个进程然后该进程会创建一个线程来运行文件中的代码,这个程序最初创建的线程称为主线程当主线程执行到t = threading.Thread()时会创建一个新的线程,称为子线程当前进程中的主线程与子线程由CPU进行调度,并发地运行,具体调度哪个线程由操作系统的调度算法决定子线程在运行时,主线程不会等待子线程,而是继续向下执行,直到执行到文件末尾没有代码时,主线程会等待子线程运行结束后再退出 thread_object.start()
t = threading.Thread()只是创建了一个线程,并不会执行线程代码
t.start()使线程t达到就绪态,等待CPU进行调度,具体何时调度由CPU决定
以上面的并发任务的代码为例,先注释掉t1.start()和t2.start()
import timeimport threadingdef work_1():print('任务1...')time.sleep(2)def work_2():print('任务2...')time.sleep(2)# 通过 Thread 类创建线程对象, 并使用 target 绑定线程对象要运行的任务t1 = threading.Thread(target=work_1)t2 = threading.Thread(target=work_2)# 运行线程start = time.time()t1.start()t2.start()# t1.join()# t2.join()end = time.time()print(f'总共用时: {end - start} s') 任务1...任务2...总共用时: 0.0009987354278564453 s 可以看到主线程没有等待子线程,而是继续向下执行当执行到end = time.time()时,此时end记录的时间是主线程运行到这行代码的时间之后运行print(f'总共用时: {end - start} s'),输出时间0.0009987354278564453 s,此时执行到了文件末尾没有其他代码,主线程会等待子线程运行结束后再退出为了能正确记录线程运行的时间,我们需要让主线程等待子线程 thread_object.join() t.join()使主线程等待子线程,子线程任务执行结束后主线程再继续向下执行仍然以上面的并发任务的代码为例,取消注释t1.start()和t2.start() import timeimport threadingdef work_1():print('任务1...')time.sleep(2)def work_2():print('任务2...')time.sleep(2)# 通过 Thread 类创建线程对象, 并使用 target 绑定线程对象要运行的任务t1 = threading.Thread(target=work_1)t2 = threading.Thread(target=work_2)# 运行线程start = time.time()t1.start()t2.start()t1.join()t2.join()end = time.time()print(f'总共用时: {end - start} s') 任务1...任务2...总共用时: 2.008962392807007 s 可以看到主线程等待子线程运行结束后才继续向下执行,正确记录了子线程运行的时间 thread_object.setDaemon() 设置守护线程,需要在线程启动之前进行设置如果一个线程是守护线程,那么主线程运行结束后不论子线程任务是否结束都会自动退出没有设置守护线程的情况 import timeimport threadingdef work():for i in range(5):print(i)time.sleep(1)t = threading.Thread(target=work)# t.setDaemon(True)t.start()print('主线程即将退出...') 0主线程即将退出...1234 设置守护线程的情况 import timeimport threadingdef work():for i in range(5):print(i)time.sleep(1)t = threading.Thread(target=work)t.setDaemon(True)t.start()print('主线程即将退出...') 0主线程即将退出... 可以看到并没有继续输出 1 1 1、 2 2 2、 3 3 3、 4 4 4,主线程就退出了 thread_object.current_thread() 获取当前线程对象的引用可以用来获取线程名称 import threadingdef work():name = threading.current_thread().name # getName()print(name)for i in range(5):t = threading.Thread(target=work)t.name = f'线程-{i}' # setName(f'线程-{i}')t.start()