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

Python多线程详细体验

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

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

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

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

一、创建线程的方式-threading

方法1

在实例化一个线程对象时,将要执行的任务函数以参数的形式传入threading:FUd28资讯网——每日最新资讯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() 构造器,可以用来创建线程:FUd28资讯网——每日最新资讯28at.com

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

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

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

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

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

方法2

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

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

FUd28资讯网——每日最新资讯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...

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

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

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

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

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

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

三、守护线程(Daemon Thread)

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

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

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

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

若将两个子线程的其中一个设置为守护线程,另一个设置为非守护线程:FUd28资讯网——每日最新资讯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...

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

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

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

四、join()方法

不使用join方法

当设置多个线程时,在一般情况下(无守护线程,setDeamon=False),多个线程同时启动,主线程执行完,会等待其他子线程执行完,程序才会退出。FUd28资讯网——每日最新资讯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

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

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

使用join()方法

主线程任务结束之后,进入阻塞状态,一直等待调用join方法的子线程执行结束之后,主线程才会终止。下面的例子是让t调用join()方法。FUd28资讯网——每日最新资讯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

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

join()方法的timeout参数

join的语法结构为join(timeout=None),可以看到join()方法有一个timeout参数,其默认值为None,而参数timeout可以进行赋值,其含义是指定等待被join的线程的时间最长为timeout秒,也就是说当在timeout秒内被join的线程还没有执行结束的话,就不再进行等待了。FUd28资讯网——每日最新资讯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在工业自动化领域的应用详解

标签:
  • 热门焦点
  • 官方承诺:K60至尊版将会首批升级MIUI 15

    全新的MIUI 15今天也有了消息,在官宣了K60至尊版将会搭载天玑9200+处理器和独显芯片X7的同时,Redmi给出了官方承诺,K60至尊重大更新首批升级,会首批推送MIUI 15。也就是说虽然
  • 小米官宣:2023年上半年出货量中国第一!

    今日早间,小米电视官方微博带来消息,称2023年小米电视上半年出货量达到了中国第一,同时还表示小米电视的巨屏风暴即将开始。“公布一个好消息2023年#小米电视上半年出货量中国
  • 得物效率前端微应用推进过程与思考

    一、背景效率工程随着业务的发展,组织规模的扩大,越来越多的企业开始意识到协作效率对于企业团队的重要性,甚至是决定其在某个行业竞争中突围的关键,是企业长久生存的根本。得物
  • 每天一道面试题-CPU伪共享

    前言:了不起:又到了每天一到面试题的时候了!学弟,最近学习的怎么样啊 了不起学弟:最近学习的还不错,每天都在学习,每天都在进步! 了不起:那你最近学习的什么呢? 了不起学弟:最近在学习C
  • 品牌洞察丨服务本地,美团直播成效几何?

    来源:17PR7月11日,美团App首页推荐位出现“美团直播”的固定入口。在直播聚合页面,外卖“神枪手”直播间、美团旅行直播间、美团买菜直播间等均已上线,同时
  • ESG的面子与里子

    来源 | 光子星球撰文 | 吴坤谚编辑 | 吴先之三伏大幕拉起,各地高温预警不绝,但处于厄尔尼诺大“烤”之下的除了众生,还有各大企业发布的ESG报告。ESG是“环境保
  • 华为发布HarmonyOS 4:更好玩、更流畅、更安全

    在8月4日的华为开发者大会2023(HDC.Together)大会上,HarmonyOS 4正式发布。自2019年发布以来,HarmonyOS一直以用户为中心,经历四年多的发展HarmonyOS已
  • 华为开发者大会2023日程公开:开设鸿蒙HarmonyOS 4体验区

    IT之家 7 月 31 日消息,华为今日公布了 HDC.Together 开发者大会 2023 的详细日程。整场大会将于 8 月 4 日-6 日之间举行,届时将发布最新一代鸿蒙 H
  • 与兆芯合作 联想推出全新旗舰版笔记本电脑开天N7系列

    联想与兆芯合作推出全新联想旗舰版笔记本电脑开天 N7系列。这个系列采用兆芯KX-6640MA处理器平台,KX-6640MA 处理器是采用了陆家嘴架构,16nm 工艺,4 核 4 线
Top