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

Python系列:多线程(threading)的学习和使用

来源: 责编: 时间:2024-01-15 17:11:10 273观看
导读哈喽大家好,我是了不起,今天来给大家介绍关于Python中的线程,threading库。引言在Python中,threading库提供了一种简单且方便的方式来实现多线程编程。通过使用线程,可以在程序中并行执行多个任务,提高程序的性能和响应性。

哈喽大家好,我是了不起,今天来给大家介绍关于Python中的线程,threading库。kb528资讯网——每日最新资讯28at.com

引言

在Python中,threading库提供了一种简单且方便的方式来实现多线程编程。通过使用线程,可以在程序中并行执行多个任务,提高程序的性能和响应性。kb528资讯网——每日最新资讯28at.com

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

了解线程

线程是程序执行的最小单元,是操作系统能够进行运算调度的基本单位。与进程不同,线程在同一进程下共享相同的内存空间,因此线程之间的通信更加方便。在Python中,threading库提供了对线程的支持。kb528资讯网——每日最新资讯28at.com

创建线程

threading库是Python中的标准库,无需下载,我们只需在文件中导入threading库就可以用了。kb528资讯网——每日最新资讯28at.com

创建线程的时候主要有两种方式,第一种是通过继承threading.Thread类,第二种则是通过传递可调用对象给threading.Thread的构造函数,接下来先讲解第一种方式。kb528资讯网——每日最新资讯28at.com

1.通过继承threading.Thread类创建线程

import threadingclass MyThread(threading.Thread):    def __init__(self, name):        super(MyThread, self).__init__()        self.name = name    def run(self):        print(f"Thread {self.name} is running.")# 创建线程的实例thread1 = MyThread(name="Thread 1")thread2 = MyThread(name="Thread 2")# 启动线程thread1.start()thread2.start()# 等待线程执行完毕thread1.join()thread2.join()print("Main thread is done.")

第一种方式是最常见的方式,创建线程的时候需要先创建一个类,然后继承threading.Thread,然后再我们创建的类中自定义一个方法,这里我构造的是run方法,在这个方法中我们可以去实现线程需要执行的主要逻辑。kb528资讯网——每日最新资讯28at.com

然后通过thread1和thread2创建对应的构造实例,使用线程中的start()方法去启动线程,最后在使用join()等到线程执行完毕,这样我们创建了一个基本的多线程,执行后结果如下:kb528资讯网——每日最新资讯28at.com

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

然后我们再来了解第二种创建线程的方式。kb528资讯网——每日最新资讯28at.com

2.通过传递可调用对象创建线程

import threadingdef my_function(name):    print(f"Thread {name} is running.")# 创建线程的实例,传递一个可调用对象和参数thread1 = threading.Thread(target=my_function, args=("Thread 1",))thread2 = threading.Thread(target=my_function, args=("Thread 2",))# 启动线程thread1.start()thread2.start()# 等待线程执行完毕thread1.join()thread2.join()print("Main thread is done.")

这种方式我们是直接通过传递给一个可调用对象给threading.Thread的构造函数,我们所传递的这个可执行对象可以是函数、方法、或者是__call__等方法类的实例,kb528资讯网——每日最新资讯28at.com

其中在threading.Thread实例中,通过使用target参数指定我们需要调用的对象,注意这里指定调用对象是不需要加括号,直接传需要调用的可执行对象名就行,后面就和上面一样,通过使用start()方法和join()方法,执行结果也是跟第一种方式一样。kb528资讯网——每日最新资讯28at.com

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

以上两种方式都可以创建线程,选择那种一般取决于个人在项目中的代码风格和偏好,但是最终都是需要确保的是,无论使用哪种方式我们都需要保证在调用的方法中包含有线程的主要逻辑。kb528资讯网——每日最新资讯28at.com

线程同步

Python中的线程和其他语言中的线程逻辑也是一样,如果创建了多个线程,那么这几个线程就是共享内存,可能会导致数据竞争和不确定的结果,所以我们需要在线程中加锁(lock)。kb528资讯网——每日最新资讯28at.com

1.锁的基本用法

在python中,如果需要对线程加锁我们就需要用到threading.lock()这个方法:kb528资讯网——每日最新资讯28at.com

import threading# 共享资源counter = 0# 创建锁对象my_lock = threading.Lock()def increment_counter():    global counter    for _ in range(1000000):        with my_lock:            counter += 1# 创建两个线程,分别增加计数器的值thread1 = threading.Thread(target=increment_counter)thread2 = threading.Thread(target=increment_counter)# 启动线程thread1.start()thread2.start()# 等待两个线程执行完毕thread1.join()thread2.join()print(f"Final counter value: {counter}")

在上述代码中,我们通过创建了一个全局锁对象,然后在调用的可执行对象中,使用with语句来获取锁和释放锁,以此来确保线程共享的资源是原子的。这样可以避免多个线程对counter的参数结果进行数据竞争。kb528资讯网——每日最新资讯28at.com

从这个简单的代码上我们可能看不出执行后实际有什么不同,接下来我举一个例子来说明没有加锁和加了锁后的执行结果。kb528资讯网——每日最新资讯28at.com

2.不加锁执行

import threadingclass BankAccount:    def __init__(self, balance):        self.balance = balance    def withdraw(self, amount):        current_balance = self.balance        new_balance = current_balance - amount        # 模拟取款操作的延迟        threading.Event().wait(0.1)        self.balance = new_balance        return new_balance# 创建一个共享的银行账户account = BankAccount(balance=1000)def withdraw_from_account(account, amount):    for _ in range(3):        new_balance = account.withdraw(amount)        print(f"Withdraw {amount}, New Balance: {new_balance}")# 创建两个线程进行取款操作thread1 = threading.Thread(target=withdraw_from_account, args=(account, 100))thread2 = threading.Thread(target=withdraw_from_account, args=(account, 150))# 启动两个线程thread1.start()thread2.start()# 等待两个线程执行完毕thread1.join()thread2.join()print(f"Final Balance: {account.balance}")

执行结果:kb528资讯网——每日最新资讯28at.com

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

在上面这个不加锁的实例中,我们用withdraw方法来模拟取款操作,然后通过两个线程来对同时对账户进行取款操作,但是由于这个实例中没有加锁,就会出现下面的情况:kb528资讯网——每日最新资讯28at.com

  • thread1读取了账户余额(假设为1000)。
  • thread2也读取了相同的账户余额(仍然是1000)。
  • thread1执行取款操作,更新了账户余额为900。
  • thread2执行取款操作,更新了账户余额为850。

就这样,本来是同一个账户,但是两个线程都是各管各的,最后导致两个线程都取了3次钱后,最后得出的结果是账户里面还剩了550元。kb528资讯网——每日最新资讯28at.com

接下来我们再看看加锁后的执行结果:kb528资讯网——每日最新资讯28at.com

import threadingclass BankAccount:    def __init__(self, balance):        self.balance = balance        self.lock = threading.Lock()    def withdraw(self, amount):        with self.lock:            current_balance = self.balance            new_balance = current_balance - amount            # 模拟取款操作的延迟            threading.Event().wait(0.1)            self.balance = new_balance            return new_balance# 创建一个共享的银行账户account = BankAccount(balance=1000)def withdraw_from_account(account, amount):    for _ in range(3):        new_balance = account.withdraw(amount)        print(f"Withdraw {amount}, New Balance: {new_balance}")# 创建两个线程进行取款操作thread1 = threading.Thread(target=withdraw_from_account, args=(account, 100))thread2 = threading.Thread(target=withdraw_from_account, args=(account, 150))# 启动两个线程thread1.start()thread2.start()# 等待两个线程执行完毕thread1.join()thread2.join()print(f"Final Balance: {account.balance}")

同样的实例,我们通过在实例中加锁后再去执行,结果如下:kb528资讯网——每日最新资讯28at.com

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

通过在实例中添加with self.lock后,我们保证了两个线程访问余额blance的原子性,不管是有多少个线程,每个线程访问的余额始终是其他线程取钱后的最新结果,这样就保证了代码程序执行后的结果是正确的。kb528资讯网——每日最新资讯28at.com

以上是今天分享的关于Python中一些基本的线程使用,有兴趣的小伙伴想要深入学习threading这个模块的话可以在留言区打出threading,人多的话我下期就继续更新这个模块。kb528资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-61905-0.htmlPython系列:多线程(threading)的学习和使用

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 高可靠的跨系统转账如何设计

下一篇: REST API的艺术:初学者穿越API空间的旅程与速查表!

标签:
  • 热门焦点
  • Find N3入网:最高支持16+1TB

    OPPO将于近期登场的Find N3折叠屏目前已经正式入网,型号为PHN110。本次Find N3在外观方面相比前两代有很大的变化,不再是小号的横向折叠屏,而是跟别的厂商一样采用了较为常见的
  • Redmi Pad评测:红米充满野心的一次尝试

    从Note系列到K系列,从蓝牙耳机到笔记本电脑,红米不知不觉之间也已经形成了自己颇有竞争力的产品体系,在中端和次旗舰市场上甚至要比小米新机的表现来得更好,正所谓“大丈夫生居
  • K8S | Service服务发现

    一、背景在微服务架构中,这里以开发环境「Dev」为基础来描述,在K8S集群中通常会开放:路由网关、注册中心、配置中心等相关服务,可以被集群外部访问;图片对于测试「Tes」环境或者
  • Java NIO内存映射文件:提高文件读写效率的优秀实践!

    Java的NIO库提供了内存映射文件的支持,它可以将文件映射到内存中,从而可以更快地读取和写入文件数据。本文将对Java内存映射文件进行详细的介绍和演示。内存映射文件概述内存
  • 只需五步,使用start.spring.io快速入门Spring编程

    步骤1打开https://start.spring.io/,按照屏幕截图中的内容创建项目,添加 Spring Web 依赖项,并单击“生成”按钮下载 .zip 文件,为下一步做准备。请在进入步骤2之前进行解压。图
  • 虚拟键盘 API 的妙用

    你是否在遇到过这样的问题:移动设备上有一个固定元素,当激活虚拟键盘时,该元素被隐藏在了键盘下方?多年来,这一直是 Web 上的默认行为,在本文中,我们将探讨这个问题、为什么会发生
  • 自律,给不了Keep自由!

    来源 | 互联网品牌官作者 | 李大为编排 | 又耳 审核 | 谷晓辉自律能不能给用户自由暂时不好说,但大概率不能给Keep自由。近日,全球最大的在线健身平台Keep正式登陆港交所,努力
  • 小米公益基金会捐赠2500万元驰援北京、河北暴雨救灾

    8月2日消息,今日小米科技创始人雷军在其微博上发布消息称,小米公益基金会宣布捐赠2500万元驰援北京、河北暴雨救灾。携手抗灾,京冀安康!以下为公告原文
  • OPPO K11搭载长寿版100W超级闪充:26分钟充满100%

    据此前官方宣布,OPPO将于7月25日也就是今天下午14:30举办新品发布会,届时全新的OPPO K11将正式与大家见面,将主打旗舰影像,和同档位竞品相比,其最大的卖
Top