当前位置:首页 > 科技  > 软件

Python多线程详细体验

来源: 责编: 时间:2023-10-08 07:06:30 382观看
导读线程是处理器调度和分配的基本单位,进程则作为资源拥有的基本单位。每个进程是由私有的虚拟地址空间、代码、数据和其它各种系统资源组成。线程是进程内部的一个执行单元。每一个进程至少有一个主执行线程,它无需由用户

线程是处理器调度和分配的基本单位,进程则作为资源拥有的基本单位。每个进程是由私有的虚拟地址空间、代码、数据和其它各种系统资源组成。线程是进程内部的一个执行单元。每一个进程至少有一个主执行线程,它无需由用户去主动创建,是由系统自动创建的。用户根据需要在应用程序中创建其它线程,多个线程并发地运行于同一个进程中。uCq28资讯网——每日最新资讯28at.com

uCq28资讯网——每日最新资讯28at.com

uCq28资讯网——每日最新资讯28at.com

一、创建线程的方式-threading

方法1

在实例化一个线程对象时,将要执行的任务函数以参数的形式传入threading:uCq28资讯网——每日最新资讯28at.com

# -*- 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() 构造器,可以用来创建线程:uCq28资讯网——每日最新资讯28at.com

__init__(self, group=None, target=None, name=None, args=(), kwargs=None, *,daemon=None)

uCq28资讯网——每日最新资讯28at.com

此构造方法中,以上所有参数都是可选参数,即可以使用,也可以忽略。其中各个参数的含义如下:uCq28资讯网——每日最新资讯28at.com

  • group:指定所创建的线程隶属于哪个线程组(此参数尚未实现,无需调用);
  • target:指定所创建的线程要调度的目标方法(最常用);
  • args:以元组的方式,为 target 指定的方法传递参数;
  • kwargs:以字典的方式,为 target 指定的方法传递参数;
  • daemon:指定所创建的线程是否为后代线程。

这些参数,初学者只需记住 target、args、kwargs 这 3 个参数的功能即可。但是线程需要手动启动才能运行,threading 模块提供了 start() 方法用来启动线程。因此在上面程序的基础上,添加如下语句:t.start()uCq28资讯网——每日最新资讯28at.com

方法2

通过继承 Thread 类,我们可以自定义一个线程类,从而实例化该类对象,获得子线程。uCq28资讯网——每日最新资讯28at.com

需要注意的是,在创建 Thread 类的子类时,必须重写从父类继承得到的 run() 方法。因为该方法即为要创建的子线程执行的方法,其功能如同第一种创建方法中的 printNumber() 自定义函数。uCq28资讯网——每日最新资讯28at.com

# -*- 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...

uCq28资讯网——每日最新资讯28at.com

二、主线程和子线程

# -*- 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...

uCq28资讯网——每日最新资讯28at.com

注意: 第一次t.start()后,当前存在两个线程(主线程+子线程),第二次t.start()的时候又创建了一个子线程所以当前存在三个线程。uCq28资讯网——每日最新资讯28at.com

如果程序中不显式创建任何线程,则所有程序的执行,都将由主线程 MainThread 完成,程序就只能按照顺序依次执行。uCq28资讯网——每日最新资讯28at.com

uCq28资讯网——每日最新资讯28at.com

此程序中,子线程 Thread-1和Thread-2 执行的是 run() 方法中的代码,而 MainThread 执行的是主程序中的代码,它们以快速轮换 CPU 的方式在执行。uCq28资讯网——每日最新资讯28at.com

uCq28资讯网——每日最新资讯28at.com

三、守护线程(Daemon Thread)

守护线程(Daemon Thread)也叫后台进程,它的目的是为其他线程提供服务。如果其他线程被杀死了,那么守护线程也就没有了存在的必要。因此守护线程会随着非守护线程的消亡而消亡。Thread类中,子线程被创建时默认是非守护线程,我们可以通过setDaemon(True)将一个子线程设置为守护线程。uCq28资讯网——每日最新资讯28at.com

# -*- 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

将两个子线程改写为守护线程,因为当主程序中的代码执行完后,主线程就可以结束了,这时候被设定为守护线程的两个子线程会被杀死,然后主线程结束。uCq28资讯网——每日最新资讯28at.com

注意,当前台线程死亡后,Python 解释器会通知后台线程死亡,但是从它接收指令到做出响应需要一定的时间。如果要将某个线程设置为后台线程,则必须在该线程启动之前进行设置。也就是说,将 daemon 属性设为 True,必须在 start() 方法调用之前进行,否则会引发 RuntimeError 异常。uCq28资讯网——每日最新资讯28at.com

uCq28资讯网——每日最新资讯28at.com

若将两个子线程的其中一个设置为守护线程,另一个设置为非守护线程:uCq28资讯网——每日最新资讯28at.com

# -*- 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...

uCq28资讯网——每日最新资讯28at.com

此时非守护线程作为前台程序还在继续执行,守护线程就还有“守护”的意义,就会继续执行。uCq28资讯网——每日最新资讯28at.com

uCq28资讯网——每日最新资讯28at.com

四、join()方法

不使用join方法

当设置多个线程时,在一般情况下(无守护线程,setDeamon=False),多个线程同时启动,主线程执行完,会等待其他子线程执行完,程序才会退出。uCq28资讯网——每日最新资讯28at.com

# -*- 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

uCq28资讯网——每日最新资讯28at.com

我们的计时是对主线程计时,主线程结束,计时随之结束,打印出主线程的用时。主线程的任务完成之后,主线程随之结束,子线程继续执行自己的任务,直到全部的子线程的任务全部结束,程序结束。uCq28资讯网——每日最新资讯28at.com

使用join()方法

主线程任务结束之后,进入阻塞状态,一直等待调用join方法的子线程执行结束之后,主线程才会终止。下面的例子是让t调用join()方法。uCq28资讯网——每日最新资讯28at.com

# -*- 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

uCq28资讯网——每日最新资讯28at.com

join()方法的timeout参数

join的语法结构为join(timeout=None),可以看到join()方法有一个timeout参数,其默认值为None,而参数timeout可以进行赋值,其含义是指定等待被join的线程的时间最长为timeout秒,也就是说当在timeout秒内被join的线程还没有执行结束的话,就不再进行等待了。uCq28资讯网——每日最新资讯28at.com

# -*- 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

上一篇: Go 并发可视化解释 - Semaphore

下一篇: Python在工业自动化领域的应用详解

标签:
  • 热门焦点
  • 一加Ace2 Pro真机揭晓 钛空灰配色质感拉满

    终于,在经过了几波预热之后,一加Ace2 Pro的外观真机图在网上出现了。还是博主数码闲聊站曝光的,这次的外观设计还是延续了一加11的方案,只是细节上有了调整,例如新加入了钛空灰
  • Redmi Pad评测:红米充满野心的一次尝试

    从Note系列到K系列,从蓝牙耳机到笔记本电脑,红米不知不觉之间也已经形成了自己颇有竞争力的产品体系,在中端和次旗舰市场上甚至要比小米新机的表现来得更好,正所谓“大丈夫生居
  • 28个SpringBoot项目中常用注解,日常开发、求职面试不再懵圈

    前言在使用SpringBoot开发中或者在求职面试中都会使用到很多注解或者问到注解相关的知识。本文主要对一些常用的注解进行了总结,同时也会举出具体例子,供大家学习和参考。注解
  • 量化指标是与非:挽救被量化指标扼杀的技术团队

    作者 | 刘新翠整理 | 徐杰承本文整理自快狗打车技术总监刘新翠在WOT2023大会上的主题分享,更多精彩内容及现场PPT,请关注51CTO技术栈公众号,发消息【WOT2023PPT】即可直接领取
  • 使用LLM插件从命令行访问Llama 2

    最近的一个大新闻是Meta AI推出了新的开源授权的大型语言模型Llama 2。这是一项非常重要的进展:Llama 2可免费用于研究和商业用途。(几小时前,swyy发现它已从LLaMA 2更名为Lla
  • 为什么你不应该使用Div作为可点击元素

    按钮是为任何网络应用程序提供交互性的最常见方式。但我们经常倾向于使用其他HTML元素,如 div span 等作为 clickable 元素。但通过这样做,我们错过了许多内置浏览器的功能。
  • 每天一道面试题-CPU伪共享

    前言:了不起:又到了每天一到面试题的时候了!学弟,最近学习的怎么样啊 了不起学弟:最近学习的还不错,每天都在学习,每天都在进步! 了不起:那你最近学习的什么呢? 了不起学弟:最近在学习C
  • AI芯片初创公司Tenstorrent获三星和现代1亿美元投资

    Tenstorrent是一家由芯片行业资深人士Jim Keller领导的加拿大初创公司,专注于开发人工智能芯片,该公司周三表示,已经从现代汽车集团和三星投资基金等
  • 联想的ThinkBook Plus下一版曝光,键盘旁边塞个平板

    ThinkBook Plus 是联想的一个特殊笔记本类别,它在封面放入了一块墨水屏,也给人留下了较为深刻的印象。据有人爆料,联想的下一款 ThinkBook Plus 可能更特殊,它
Top