线程是处理器调度和分配的基本单位,进程则作为资源拥有的基本单位。每个进程是由私有的虚拟地址空间、代码、数据和其它各种系统资源组成。线程是进程内部的一个执行单元。每一个进程至少有一个主执行线程,它无需由用户去主动创建,是由系统自动创建的。用户根据需要在应用程序中创建其它线程,多个线程并发地运行于同一个进程中。
在实例化一个线程对象时,将要执行的任务函数以参数的形式传入threading:
# -*- coding: utf-8 -*-import timeimport threadingimport datetimedef printNumber(n: int) -> None: while True: times = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') print(f'{times}-{n}') time.sleep(n)for i in range(1, 3): t = threading.Thread(target=printNumber, args=(i,)) t.start()# 输出2022-12-16 11:04:40-12022-12-16 11:04:40-22022-12-16 11:04:41-12022-12-16 11:04:42-22022-12-16 11:04:42-12022-12-16 11:04:43-12022-12-16 11:04:44-22022-12-16 11:04:44-12022-12-16 11:04:45-12022-12-16 11:04:46-22022-12-16 11:04:46-12022-12-16 11:04:47-1....Process finished with exit code -1
创建两个线程,一个线程每隔一秒打印一个“1”,另一个线程每隔2秒打印一个“2”Thread 类提供了如下的 init() 构造器,可以用来创建线程:
__init__(self, group=None, target=None, name=None, args=(), kwargs=None, *,daemon=None)
此构造方法中,以上所有参数都是可选参数,即可以使用,也可以忽略。其中各个参数的含义如下:
这些参数,初学者只需记住 target、args、kwargs 这 3 个参数的功能即可。但是线程需要手动启动才能运行,threading 模块提供了 start() 方法用来启动线程。因此在上面程序的基础上,添加如下语句:t.start()
通过继承 Thread 类,我们可以自定义一个线程类,从而实例化该类对象,获得子线程。
需要注意的是,在创建 Thread 类的子类时,必须重写从父类继承得到的 run() 方法。因为该方法即为要创建的子线程执行的方法,其功能如同第一种创建方法中的 printNumber() 自定义函数。
# -*- coding: utf-8 -*-import datetimeimport timeimport threadingclass MyThread(threading.Thread): def __init__(self, n): self.n = n # 注意:一定要调用父类的初始化函数 super().__init__() def run(self) -> None: while True: times = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') print(f'{times}-{self.n}') time.sleep(self.n)for i in range(1, 3): t = MyThread(i) t.start()# 输出2022-12-16 12:43:24-12022-12-16 12:43:24-22022-12-16 12:43:25-12022-12-16 12:43:26-22022-12-16 12:43:26-12022-12-16 12:43:27-12022-12-16 12:43:28-2...
# -*- coding: utf-8 -*-import datetimeimport timeimport threadingclass MyThread(threading.Thread): def __init__(self, n): self.n = n # 注意:一定要调用父类的初始化函数,否则无法创建线程 super().__init__() def run(self) -> None: while True: _count = threading.active_count() threading_name = threading.current_thread().getName() times = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') print(f'{times}-{self.n}-"当前活跃的线程个数:{_count}"-"当前线程的名称是":{threading_name}') time.sleep(self.n)for i in range(1, 3): t = MyThread(i) t.start() print(threading.current_thread().getName())# 输出2022-12-16 13:18:00-1-"当前活跃的线程个数:2"-"当前线程的名称是":Thread-1MainThread2022-12-16 13:18:00-2-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-2MainThread2022-12-16 13:18:01-1-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-12022-12-16 13:18:02-2-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-22022-12-16 13:18:02-1-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-12022-12-16 13:18:03-1-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-12022-12-16 13:18:04-2-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-22022-12-16 13:18:04-1-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-1...
注意: 第一次t.start()后,当前存在两个线程(主线程+子线程),第二次t.start()的时候又创建了一个子线程所以当前存在三个线程。
如果程序中不显式创建任何线程,则所有程序的执行,都将由主线程 MainThread 完成,程序就只能按照顺序依次执行。
此程序中,子线程 Thread-1和Thread-2 执行的是 run() 方法中的代码,而 MainThread 执行的是主程序中的代码,它们以快速轮换 CPU 的方式在执行。
守护线程(Daemon Thread)也叫后台进程,它的目的是为其他线程提供服务。如果其他线程被杀死了,那么守护线程也就没有了存在的必要。因此守护线程会随着非守护线程的消亡而消亡。Thread类中,子线程被创建时默认是非守护线程,我们可以通过setDaemon(True)将一个子线程设置为守护线程。
# -*- coding: utf-8 -*-import datetimeimport timeimport threadingclass MyThread(threading.Thread): def __init__(self, n): self.n = n # 注意:一定要调用父类的初始化函数,否则无法创建线程 super().__init__() def run(self) -> None: while True: _count = threading.active_count() threading_name = threading.current_thread().getName() times = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') print(f'{times}-{self.n}-"当前活跃的线程个数:{_count}"-"当前线程的名称是":{threading_name}') time.sleep(self.n)for i in range(1, 3): t = MyThread(i) t.setDaemon(True) t.start() print(threading.current_thread().getName()) # 输出2022-12-16 13:27:46-1-"当前活跃的线程个数:2"-"当前线程的名称是":Thread-1MainThread2022-12-16 13:27:46-2-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-2MainThread
将两个子线程改写为守护线程,因为当主程序中的代码执行完后,主线程就可以结束了,这时候被设定为守护线程的两个子线程会被杀死,然后主线程结束。
注意,当前台线程死亡后,Python 解释器会通知后台线程死亡,但是从它接收指令到做出响应需要一定的时间。如果要将某个线程设置为后台线程,则必须在该线程启动之前进行设置。也就是说,将 daemon 属性设为 True,必须在 start() 方法调用之前进行,否则会引发 RuntimeError 异常。
若将两个子线程的其中一个设置为守护线程,另一个设置为非守护线程:
# -*- coding: utf-8 -*-import datetimeimport timeimport threadingclass MyThread(threading.Thread): def __init__(self, n): self.n = n # 注意:一定要调用父类的初始化函数,否则无法创建线程 super().__init__() def run(self) -> None: while True: _count = threading.active_count() threading_name = threading.current_thread().getName() times = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') print(f'{times}-{self.n}-"当前活跃的线程个数:{_count}"-"当前线程的名称是":{threading_name}') time.sleep(self.n)for i in range(1, 3): t = MyThread(i) if i == 1: t.setDaemon(True) t.start() print(threading.current_thread().getName()) # 输出2022-12-16 13:30:17-1-"当前活跃的线程个数:2"-"当前线程的名称是":Thread-1MainThread2022-12-16 13:30:17-2-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-2MainThread2022-12-16 13:30:18-1-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-12022-12-16 13:30:19-1-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-12022-12-16 13:30:19-2-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-22022-12-16 13:30:20-1-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-1...
此时非守护线程作为前台程序还在继续执行,守护线程就还有“守护”的意义,就会继续执行。
当设置多个线程时,在一般情况下(无守护线程,setDeamon=False),多个线程同时启动,主线程执行完,会等待其他子线程执行完,程序才会退出。
# -*- coding: utf-8 -*-import datetimeimport timeimport threadingclass MyThread(threading.Thread): def __init__(self, n): self.n = n # 注意:一定要调用父类的初始化函数,否则无法创建线程 super().__init__() def run(self) -> None: _count = threading.active_count() threading_name = threading.current_thread().getName() times = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') time.sleep(1) print(f'{times}-{self.n}-"当前活跃的线程个数:{_count}"-"当前线程的名称是":{threading_name}')start_time = time.time()print(f'{start_time},这是主线程:', threading.current_thread().name)for i in range(5): t = MyThread(i) # t.setDaemon(True) t.start() # t.join()end_time = time.time()print(f'{end_time},主线程结束了!', threading.current_thread().name)print('一共用时:', end_time - start_time)# 输出1671174404.6552384,这是主线程:MainThread1671174404.656239,主线程结束了!MainThread一共用时:0.00100064277648925782022-12-16 15:06:44-0-"当前活跃的线程个数:2"-"当前线程的名称是":Thread-12022-12-16 15:06:44-1-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-22022-12-16 15:06:44-2-"当前活跃的线程个数:4"-"当前线程的名称是":Thread-32022-12-16 15:06:44-3-"当前活跃的线程个数:5"-"当前线程的名称是":Thread-42022-12-16 15:06:44-4-"当前活跃的线程个数:6"-"当前线程的名称是":Thread-5
我们的计时是对主线程计时,主线程结束,计时随之结束,打印出主线程的用时。主线程的任务完成之后,主线程随之结束,子线程继续执行自己的任务,直到全部的子线程的任务全部结束,程序结束。
主线程任务结束之后,进入阻塞状态,一直等待调用join方法的子线程执行结束之后,主线程才会终止。下面的例子是让t调用join()方法。
# -*- coding: utf-8 -*-import datetimeimport timeimport threadingclass MyThread(threading.Thread): def __init__(self, n): self.n = n # 注意:一定要调用父类的初始化函数,否则无法创建线程 super().__init__() def run(self) -> None: _count = threading.active_count() threading_name = threading.current_thread().getName() times = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') time.sleep(1) print(f'{times}-{self.n}-"当前活跃的线程个数:{_count}"-"当前线程的名称是":{threading_name}')start_time = time.time()print(f'{start_time},这是主线程:', threading.current_thread().name)for i in range(5): t = MyThread(i) # t.setDaemon(True) t.start() t.join()end_time = time.time()print(f'{end_time},主线程结束了!', threading.current_thread().name)print('一共用时:', end_time - start_time)# 输出1671174502.0245655,这是主线程:MainThread2022-12-16 15:08:22-0-"当前活跃的线程个数:2"-"当前线程的名称是":Thread-12022-12-16 15:08:23-1-"当前活跃的线程个数:2"-"当前线程的名称是":Thread-22022-12-16 15:08:24-2-"当前活跃的线程个数:2"-"当前线程的名称是":Thread-32022-12-16 15:08:25-3-"当前活跃的线程个数:2"-"当前线程的名称是":Thread-42022-12-16 15:08:26-4-"当前活跃的线程个数:2"-"当前线程的名称是":Thread-51671174507.0313594,主线程结束了!MainThread一共用时:5.006793975830078Process finished with exit code 0
join的语法结构为join(timeout=None),可以看到join()方法有一个timeout参数,其默认值为None,而参数timeout可以进行赋值,其含义是指定等待被join的线程的时间最长为timeout秒,也就是说当在timeout秒内被join的线程还没有执行结束的话,就不再进行等待了。
# -*- coding: utf-8 -*-import datetimeimport timeimport threadingclass MyThread(threading.Thread): def __init__(self, n): self.n = n # 注意:一定要调用父类的初始化函数,否则无法创建线程 super().__init__() def run(self) -> None: _count = threading.active_count() threading_name = threading.current_thread().getName() times = datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') time.sleep(5) print(f'{times}-{self.n}-"当前活跃的线程个数:{_count}"-"当前线程的名称是":{threading_name}')start_time = time.time()print(f'{start_time},这是主线程:', threading.current_thread().name)for i in range(5): t = MyThread(i) # t.setDaemon(True) t.start() t.join(2)end_time = time.time()print(f'{end_time},主线程结束了!', threading.current_thread().name)print('一共用时:', end_time - start_time)# 输出1671175114.663927,这是主线程:MainThread2022-12-16 15:18:34-0-"当前活跃的线程个数:2"-"当前线程的名称是":Thread-12022-12-16 15:18:36-1-"当前活跃的线程个数:3"-"当前线程的名称是":Thread-22022-12-16 15:18:38-2-"当前活跃的线程个数:4"-"当前线程的名称是":Thread-31671175124.6681008,主线程结束了!MainThread一共用时:10.0041737556457522022-12-16 15:18:40-3-"当前活跃的线程个数:4"-"当前线程的名称是":Thread-42022-12-16 15:18:42-4-"当前活跃的线程个数:4"-"当前线程的名称是":Thread-5Process finished with exit code 0
本文链接:http://www.28at.com/showinfo-26-12384-0.htmlPython多线程详细体验
声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com
下一篇: Python在工业自动化领域的应用详解