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

import threading
# 从数据库提取数据的类
class Scheduler():def __init__(self):self._lock = threading.RLock()self.start = 0# 每次取10000条数据self.step = 10000def getdata(self):# 上锁,以免多线程同时对数据库进行访问,取出重复数据self._lock.acquire()# 进行取数据操作data = 'select * from table' \'where id between self.start and self.start + self.step'# 取完数据后,指针后移self.start += self.stepself._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 threadingdef 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_idself.balance = balancedef withdraw(account, money):# 进行加锁lock.acquire()# 账户余额大于取钱数目if account.balance >= money:# 吐出钞票print(threading.current_thread().name + "取钱成功!吐出钞票:" + str(money),end=' ')# 修改余额account.balance -= moneyprint("\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 threadingdef 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 threadingdef 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 timedef 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只能由本线程进行操作。

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


技术交流

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

目前开通了技术交流群,群友已超过2000人,添加时最好的备注方式为:来源+兴趣方向,方便找到志同道合的朋友

  • 方式①、发送如下图片至微信,长按识别,后台回复:加群;
  • 方式②、添加微信号:dkl88191,备注:来自CSDN
  • 方式③、微信搜索公众号:Python学习与数据挖掘,后台回复:加群

超详细,Python 多线程总结的太到位了相关推荐

  1. 手把手讲解超详细python入门游戏项目‘打外星飞船’(四)

    手把手讲解超详细python入门游戏项目'打外星飞船'(四) 在经过创立屏幕.飞船移动和设置子弹,我们这里开始设置外形人的创建和移动.我们这里主要的任务是:创建一众外星人让它们充满屏幕,让他们向下和两 ...

  2. 神器 VS Code,超详细Python配置使用指南

    作者:Lemon 出品:Python数据之道 神器 VS Code, 超详细Python配置使用指南 大家好,我是 Lemon. 之前在公众号发了关于 PyCharm 与 VS Code 对比的文章, ...

  3. python详细安装教程-超详细Python与PyCharm安装教程,看这一篇就够了

    原标题:超详细Python与PyCharm安装教程,看这一篇就够了 学习了三天的python, 之前测试一直用课程自带的网页版玩玩, 为了学习 然后就下载了一个python和pycharm 现在分享下 ...

  4. 手把手讲解超详细python入门游戏项目‘打外星飞船’(二)

    手把手讲解超详细python入门游戏项目'打外星飞船'(二) 上次我们在(一)中创建了游戏的背景,现在我们这里将要实现用键盘控制飞船的移动.射击子弹,但是在此之前我们还有一个非常重要的部分–重构. 重 ...

  5. 手把手讲解超详细python入门游戏项目‘打外星飞船’(五)

    手把手讲解超详细python入门游戏项目'打外星飞船'(五) 这是最后一个项目了,前面我们讲了整个游戏页面的控制.飞船.子弹.外星人的创建,这里我们讨论一下子弹射杀外星人和整个游戏的结束,我们这里的文 ...

  6. 手把手讲解超详细python入门游戏项目‘打外星飞船’(三)

    手把手讲解超详细python入门游戏项目'打外星飞船'(三) 第三部分我们讲解一下飞船需要射出子弹,那么子弹的部分是怎么操作呢?接下来我直接把项目的四个文件展示出来,以注释的形式在旁边讲解.因为有很多 ...

  7. 手把手讲解超详细python入门游戏项目‘打外星飞船’(一)

    手讲解超详细python入门游戏项目'打外星飞船'手把(一) 由于内容比较多,这里会分为五篇文章来讲解,从页面的创建.飞船控制.射击.外星人创建.射杀外星人五片来展开. 做一个窗口和设置响应用户 im ...

  8. # 手把手教学超详细python通用爬虫分布式框架(一)

    手把手教学超详细python通用爬虫分布式框架(一) ` 这里日后添加系列文章的所有文章的目录 文章目录 手把手教学超详细python通用爬虫分布式框架(一) 前言 一.所谓任务? 二.任务需要什么 ...

  9. 【超详细Python教学课件分享】寻宝探路

    您好,感谢您一直对少儿编程.对"与非学堂"的关注.为了更好地为大家服务,诚邀您填写一份<关于少儿编程教与学的课件资源素材需求调查>,https://www.wjx.cn ...

最新文章

  1. SQL语句的增删改查
  2. 最大子序列和问题 hdu1231
  3. 高德软件测试工资,【高德工资】软件测试工程师待遇-看准网
  4. mysql限制类别_MySQL限制每个类别的结果
  5. LeetCode贪心 数组拆分I
  6. 第二篇 Python数据类型、字符编码、文件处理
  7. Invisible Perturbations: Physical Adversarial Examples Exploiting the Rolling Shutter Effect 论文解读
  8. Linux中mount挂载命令及其概念
  9. 计算机基础知识大全之硬件篇
  10. C语言pta————查找书籍
  11. 如何通过idea打包项目到docker
  12. 【计算机网络】什么是因特网
  13. 蓝桥杯嵌入式设计与开发历届客观题答案
  14. 景深决定照相机什么特性_什么叫光圈,什么叫景深 什么叫F值 什么叫单反相机?...
  15. linux 指定磁盘盘符,使用udev指定盘符
  16. threeJS 创建地月系
  17. SCADA系列 系统评估
  18. spring整合quartz框架定时任务实战
  19. linux系统搭建云免流,Ubuntu Server 14.04 下使用ownCloud搭建个人云服务器
  20. 蓝牙官方资料下载地址(官方)

热门文章

  1. 亚特力单片机AT32F415的ADC单次转换
  2. 我为什么相信以貌取人
  3. Apache Beam -- 简介
  4. ABAP-ALV-双击界面弹出窗口 (SALV)
  5. 编程,写代码不是关键。学习人物经验系列(一)
  6. 【详细讲解 附全部代码】【openmv控制三自由度机械臂抓取物品】硬件+软件
  7. 微软CRM与知客CRM简介及互补营销
  8. pycharm-社区版启动django项目的服务
  9. 2012年03月31日
  10. 关于安装office,出现你已安装32位,无法安装64位问题的解决方法