返回顶部
首页 > 资讯 > 后端开发 > Python >Python 多线程超详细到位总结
  • 851
分享到

Python 多线程超详细到位总结

2024-04-02 19:04:59 851人浏览 独家记忆

Python 官方文档:入门教程 => 点击学习

摘要

目录多线程threading线程池线程互斥lock与Rlock的区别技术交流在实际处理数据时,因系统内存有限,我们不可能一次把所有数据都导出进行操作,所以需要批量导出依次操作。为了加

在实际处理数据时,因系统内存有限,我们不可能一次把所有数据都导出进行操作,所以需要批量导出依次操作。为了加快运行,我们会采用多线程的方法进行数据处理,以下为我总结的多线程批量处理数据的模板:


import threading
# 从数据库提取数据的类
class Scheduler():
    def __init__(self):
        self._lock = threading.RLock()
        self.start = 0
        # 每次取10000条数据
        self.step = 10000

    def getdata(self):
        # 上,以免多线程同时对数据库进行访问,取出重复数据
        self._lock.acquire()
        # 进行取数据操作
        data = 'select * from table' \
               'where id between self.start and self.start + self.step'
        # 取完数据后,指针后移
        self.start += self.step
        self._lock.release()
        return data

# 处理数据的过程写在这里
def processdata():
    # 从该实例中提取数据
    data = scheduler.getdata()
    while data:
        # 进行处理数据的具体操作:
        # 去重、补缺、运算...只要还有数据,本线程就继续取新数据
        # 然后再获取数据,进行循环
        data = scheduler.getdata()

# 创建多线程,threads_num为创建的线程数
def threads_scheduler(threads_num):
    threads = []
    for i in range(threads_num):
        # 创建线程
        td = threading.Thread(target=processdata, name='th'+str(i+1))
        threads.append(td)
    for t in threads:
        # 启动线程
        t.start()
        for t in threads:
            # 子线程守护
            t.join()
            print('数据已全部处理成功')

if __name__=='__main__':
    # 实例化一个调度器,初始化参数
    scheduler = Scheduler()
    # 创建线程,开始处理数据
    threads_scheduler(4)

主要分为三大部分:

  • Scheduler类,负责初始化参数,getdata方法负责提取数据
  • processdata方法中写具体处理数据的流程
  • threads_scheduler方法负责创建线程

python多线程的知识我分为4部分进行讲解,以下带大家来回顾重点:

多线程threading

本章先为大家介绍了线程的相关概念:

主线程:当一个程序启动时,就有一个进程被操作系统(OS)创建,与此同时一个线程也立刻运行,该线程通常叫做程序的主线程(Main Thread)。因为它是程序开始时就执行的,如果你需要再创建线程,那么创建的线程就是这个主线程的子线程。

子线程:使用threading、ThreadPoolExecutor创建的线性均为子线程。

主线程的重要性体现在两方面:1.是产生其他子线程的线程;2.通常它必须最后完成执行,比如执行各种关闭动作。

在飞车程序中,如果没有多线程,我们就不能一边听歌一边玩飞车,听歌与玩游戏不能并行;在使用多线程后,我们就可以在玩游戏的同时听背景音乐。在这个例子中启动飞车程序就是一个进程,玩游戏和听音乐是两个线程。

Python提供了threading模块来实现多线程:threading.Thread可以创建线程;setDaemon(True)为守护主线程,默认为False;join()为守护子线程。


from time import sleep
import threading

def music(music_name):
    for i in range(2):
        print('正在听{}'.fORMat(music_name))
        sleep(1)
        print('music over')

def game(game_name):
    for i in range(2):
        print('正在玩{}'.format(game_name))
        sleep(3)
        print('game over')

threads = []
t1 = threading.Thread(target=music,args=('稻香',))
threads.append(t1)
t2 = threading.Thread(target=game,args=('飞车',))
threads.append(t2)

if __name__ == '__main__':
    for t in threads:
        # t.setDaemon(True)
        t.start()
        
    for t in threads:
        t.join()
    print('主线程运行结束')

线程池

因为新建线程系统需要分配资源、终止线程系统需要回收资源,所以如果可以重用线程,则可以减去新建/终止的开销以提升性能。同时,使用线程池的语法比自己新建线程执行线程更加简洁。

Python为我们提供了ThreadPoolExecutor来实现线程池,此线程池默认子线程守护。它的适应场景为突发性大量请求或需要大量线程完成任务,但实际任务处理时间较短。


from time import sleep
# fun为定义的待运行函数
with ThreadPoolExecutor(max_workers=5) as executor:
    ans = executor.map(fun, [遍历值])
    for res in ans:
        print(res)

with ThreadPoolExecutor(max_workers=5) as executor:
    list = [遍历值]
    ans = [executor.submit(fun, i) for i in list]
    for res in as_completed(ans):
        print(res.result())

其中max_workers为线程池中的线程个数,常用的遍历方法有map和submit+as_completed。根据业务场景的不同,若我们需要输出结果按遍历顺序返回,我们就用map方法,若想谁先完成就返回谁,我们就用submit+as_complete方法。

线程互斥

我们把一个时间段内只允许一个线程使用的资源称为临界资源,对临界资源的访问,必须互斥的进行。互斥,也称间接制约关系。线程互斥指当一个线程访问某临界资源时,另一个想要访问该临界资源的线程必须等待。当前访问临界资源的线程访问结束,释放该资源之后,另一个线程才能去访问临界资源。锁的功能就是实现线程互斥。

我把线程互斥比作厕所包间上大号的过程,因为包间里只有一个坑,所以只允许一个人进行大号。当第一个人要上厕所时,会将门上上锁,这时如果第二个人也想大号,那就必须等第一个人上完,将锁解开后才能进行,在这期间第二个人就只能在门外等着。这个过程与代码中使用锁的原理如出一辙,这里的坑就是临界资源。

Python 的 threading 模块引入了锁。threading 模块提供了 Lock 类,它有如下方法加锁和释放锁:

  • acquire():对 Lock加锁,其中timeout参数指定加锁多少秒
  • release():释放锁

class Account:
    def __init__(self, card_id, balance):
        # 封装账户ID、账户余额的两个变量
        self.card_id= card_id
        self.balance = balance
        
def withdraw(account, money):
    # 进行加锁
    lock.acquire()
    # 账户余额大于取钱数目
    if account.balance >= money:
        # 吐出钞票
        print(threading.current_thread().name + "取钱成功!吐出钞票:" + str(money),end=' ')
        # 修改余额
        account.balance -= money
        print("\t余额为: " + str(account.balance))
    else:
        print(threading.current_thread().name + "取钱失败!余额不足")
    # 进行解锁
    lock.release()
# 创建一个账户,银行卡id为8888,存款1000元
acct = Account("8888" , 1000)

# 模拟两个对同一个账户取钱
# 在主线程中创建一把锁
lock = threading.Lock()
threading.Thread(name='窗口A', target=withdraw , args=(acct , 800)).start()
threading.Thread(name='窗口B', target=withdraw , args=(acct , 800)).start()

lock与Rlock的区别

区别一:Lock被称为原始锁,一个线程只能请求一次;RLock被称为重入锁,可以被一个线程请求多次,即锁中可以嵌套锁。


import threading

def main():
    lock.acquire()
    print('第一道锁')
    lock.acquire()
    print('第二道锁')
    lock.release()
    lock.release()
    
if __name__ == '__main__':
    lock = threading.Lock()
    main()

我们会发现这个程序只会打印“第一道锁”,而且程序既没有终止,也没有继续运行。这是因为Lock锁在同一线程内第一次加锁之后还没有释放时,就进行了第二次acquire请求,导致无法执行release,所以锁永远无法释放,这就是死锁。如果我们使用RLock就能正常运行,不会发生死锁的状态。

区别二:当Lock处于锁定状态时,不属于特定线程,可在另一个线程中进行解锁释放;而RLock只有当前线程才能释放本线程上的锁,不可由其他线程进行释放,所以在使用RLock时,acquire与release必须成对出现,即解铃还须系铃人。


import threading

def main():
    lock.release()
    print("在子线程解锁后打印")
if __name__ == '__main__':
    lock = threading.Lock()
    lock.acquire()
    t = threading.Thread(target=main)
    t.start()

在主线程中定义Lock锁,然后上锁,再创建一个子线程t运行main函数释放锁,结果正常输出,说明主线程上的锁,可由子线程解锁。

如果把上面的锁改为RLock则报错。在实际中设计程序时,我们会将每个功能分别封装成一个函数,每个函数中都可能会有临界区域,所以就需要用到RLock。


import threading
import time

def fun_1():
    print('开始')
    time.sleep(1)
    lock.acquire()
    print("第一道锁")
    fun_2()
    lock.release()
    
def fun_2():
    lock.acquire()
    print("第二道锁")
    lock.release()
    
if __name__ == '__main__':
    lock = threading.RLock()
    t1 = threading.Thread(target=fun_1)
    t2 = threading.Thread(target=fun_1)
    t1.start()
    t2.start()

一句话总结就是Lock不能套娃,RLock可以套娃;Lock可以由其他线程中的锁进行操作,RLock只能由本线程进行操作。

以上就是多线程所有内容,喜欢的小伙伴支持,收藏。

技术交流

欢迎转载、收藏、有所收获点赞支持一下!

在这里插入图片描述

到此这篇关于Python 多线程超详细到位总结的文章就介绍到这了,更多相关Python 多线程内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Python 多线程超详细到位总结

本文链接: https://lsjlt.com/news/156067.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Python 多线程超详细到位总结
    目录多线程threading线程池线程互斥lock与Rlock的区别技术交流在实际处理数据时,因系统内存有限,我们不可能一次把所有数据都导出进行操作,所以需要批量导出依次操作。为了加...
    99+
    2024-04-02
  • python多线程超详细详解
    python中的多线程是一个非常重要的知识点,今天为大家对多线程进行详细的说明,代码中的注释有多线程的知识点还有测试用的实例。 import threading from thr...
    99+
    2024-04-02
  • java多线程(超详细)
    1 - 线程 1.1 - 进程 进程就是正在运行中的程序(进程是驻留在内存中的) 是系统执行资源分配和调度的独立单位 每一进程都有属于自己的存储空间和系统资源 注意:进程A和进程B的内存独立不共享。 1.2 - 线程 线程...
    99+
    2023-08-31
    jvm java intellij idea 开发语言
  • 运用示例详细总结Java多线程
    目录进程与线程Java中线程实现的方式实现 Runnable 接口继承 Thread 类Thread 类和 Runnable 接口线程的状态变化取得和设置线程的名称线程的操作方法线程...
    99+
    2024-04-02
  • JAVA多线程详解(超详细)
    目录 一、线程简介1、进程、线程2、并发、并行、串行3、进程的三态 二、线程实现1、继承Thread类2、实现Runnable接口3、实现Callable接口(不常用) 三、线程常用方法1、线程的状态2、线程常用方法 四...
    99+
    2023-08-19
    java jvm 开发语言
  • Python中多线程总结
    Python中的多线程多线程一个进程中有多个线程就是多线程。一个进程中至少有一个线程,并作为程序的入口,这个就是主线程。一个进程至少有一个主进程,其他线程称为工作线程。线程安全:线程执行一段代码,不会产生不确定的结果,那这段代码就是线程安全...
    99+
    2023-01-31
    多线程 Python
  • Java 并发(多线程)超详细
    Java 并发 此文章已收录至项目 Developer-Knowledge-Base 信息来源 https://www.cnblogs.com/snow-flower/p/6114765.html j...
    99+
    2023-09-06
    java 开发语言
  • Vue.Draggable使用文档超详细总结
    目录前言特性安装引入基础用法属性options配置项事件插槽HeaderFooter总结前言 Draggable为基于Sortable.js的vue组件,用以实现拖拽功能。 关于Vu...
    99+
    2024-04-02
  • Python 常用内置模块超详细梳理总结
    目录time模块time.sleep()time.time()time.localtime()time.strftime()datetime()random模块random.rand...
    99+
    2024-04-02
  • python 字典常用方法超详细梳理总结
    目录1.字典的概念2.字典的主要特征3.创建字典的三种方法4.字典常用方法1.clear()2.copy()3.get()4.keys()5.values()6.items()7.d...
    99+
    2024-04-02
  • python 列表常用方法超详细梳理总结
    目录列表是什么?列表常用方法1.append()2.clear()3.copy()4.count()5.extend()6.index()7.insert()8.revers ...
    99+
    2024-04-02
  • 详细总结Python类的多继承知识
    目录一、Python不同版本的类二、多继承三、多继承弊端四、Python多继承实现五、多继承的缺点六、Mixin 七、Mixin类一、Python不同版本的类 P...
    99+
    2024-04-02
  • MySQL慢查询日志超详细总结
    目录慢查询日志概念慢查询日志相关参数慢查询日志配置日志分析工具mysqldumpslow总结慢查询日志概念 MySQL的慢查询日志是MySQL提供的一种日志记录,它用来记录在MySQL中响应时间超过阀值的语句,具体指运行...
    99+
    2023-05-05
    Mysql慢查询日志解析 mysql慢查询日志在哪里 mysql慢查询日志怎么看
  • JDK8时间相关类超详细总结(含多个实例)
    目录一、带时区的时间1.获取当前时间对象(带时区)2.获取指定的时间对象(带时区)1/年月日时分秒纳秒方式指定3.通过Instant + 时区的方式指定获取时间对象4.修改时间二、D...
    99+
    2023-01-28
    jdk1.8时间类 jdk8获取时间 jdk8 时间
  • Python多线程与多进程相关知识总结
    目录一、什么是进程二、什么是线程三、并发、并行3.1 并发3.2 并行四、多线程适用场景五、Python GIL六、Python多线程、多进程实例:CPU 密集型任务6.1 单线程6...
    99+
    2024-04-02
  • python 字符串常用方法超详细梳理总结
    目录字符串是什么?字符串常用方法1.find()2.index()3.startswith()4.endswith()5.count()6.join()7.upper()8.lowe...
    99+
    2024-04-02
  • 逻辑越权总结(超详细总结涉及各类越权)
    逻辑越权总结(超详细总结涉及各类越权) 1.逻辑越权1.1.漏洞原理1.2.漏洞原因1.3.水平越权1.3.1.原理1.3.2.漏洞出现位置1.3.3.危害1.3.4.案例1.3.4.1.登录...
    99+
    2023-10-04
    安全 web安全 网络安全 php 系统安全
  • Python list(列表) 详细总结
    添加List.append('yy')  #追加到末尾 List.insert(0,'yy')  #追加到列首,适用场景是给Python添加环境变量时。 # 如: sys.path.insert(0,path)修改List[0] = 'yl...
    99+
    2023-01-31
    详细 列表 Python
  • Python运算符详细总结
    算术运算符 运算符 描述 实例 + 加 - 两个对象相加 a + b 输出结果 30 - 减 - 得到负数或是一个数减去另一个数 a - b 输出结果 -10 * 乘 - 两个数相乘或是返回一个被重复若干次的字符串 a...
    99+
    2023-01-31
    运算符 详细 Python
  • Python控制多进程与多线程并发数总结
    一、前言 本来写了脚本用于暴力破解密码,可是1秒钟尝试一个密码2220000个密码我的天,想用多线程可是只会一个for全开,难道开2220000个线程吗?只好学习控制线程数了,官方文档不好看,觉得结构不够...
    99+
    2022-06-04
    多线程 进程 Python
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作