多线程常用模板 在实际处理数据时,因系统内存有限,我们不可能一次把所有数据都导出进行操作,所以需要批量导出依次操作。为了加快运行,我们会采用多线程的方法进行数据处理,以下为我总结的多线程批量处理数据的模板:importthreading从数据库提取数据的类classScheduler():definit(self):self。lockthreading。RLock()self。start0每次取10000条数据self。step10000defgetdata(self):上锁,以免多线程同时对数据库进行访问,取出重复数据self。lock。acquire()进行取数据操作dataselectfromtablewhereidbetweenself。startandself。startself。step取完数据后,指针后移self。startself。stepself。lock。release()returndata处理数据的过程写在这里defprocessdata():从该实例中提取数据datascheduler。getdata()whiledata:进行处理数据的具体操作:去重、补缺、运算。。。只要还有数据,本线程就继续取新数据然后再获取数据,进行循环datascheduler。getdata()创建多线程,threadsnum为创建的线程数defthreadsscheduler(threadsnum):threads〔〕foriinrange(threadsnum):创建线程tdthreading。Thread(targetprocessdata,namethstr(i1))threads。append(td)fortinthreads:启动线程t。start()fortinthreads:子线程守护t。join()print(数据已全部处理成功)ifnamemain:实例化一个调度器,初始化参数schedulerScheduler()创建线程,开始处理数据threadsscheduler(4) 主要分为三大部分:Scheduler类,负责初始化参数,getdata方法负责提取数据processdata方法中写具体处理数据的流程threadsscheduler方法负责创建线程 多线程重点回顾 共分4部分对多线程的内容进行总结。多线程threading 先为大家介绍线程的相关概念:主线程:当一个程序启动时,就有一个进程被操作系统OS创建,与此同时一个线程也立刻运行,该线程通常叫做程序的主线程MainThread。因为它是程序开始时就执行的,如果你需要再创建线程,那么创建的线程就是这个主线程的子线程。子线程:使用threading、ThreadPoolExecutor创建的线性均为子线程。主线程的重要性体现在两方面:是产生其他子线程的线程通常它必须最后完成执行,比如执行各种关闭动作 在飞车程序中,如果没有多线程,我们就不能一边听歌一边玩飞车,听歌与玩游戏不能并行;在使用多线程后,我们就可以在玩游戏的同时听背景音乐。在这个例子中启动飞车程序就是一个进程,玩游戏和听音乐是两个线程。 Python提供了threading模块来实现多线程:threading。Thread可以创建线程setDaemon(True)为守护主线程,默认为Fjoin()为守护子线程。fromtimeimportsleepimportthreadingdefmusic(musicname):foriinrange(2):print(正在听{}。format(musicname))sleep(1)print(musicover)defgame(gamename):foriinrange(2):print(正在玩{}。format(gamename))sleep(3)print(gameover)threads〔〕t1threading。Thread(targetmusic,args(稻香,))threads。append(t1)t2threading。Thread(targetgame,args(飞车,))threads。append(t2)ifnamemain:fortinthreads:t。setDaemon(True)t。start()fortinthreads:t。join()print(主线程运行结束)线程池 因为新建线程系统需要分配资源、终止线程系统需要回收资源,所以如果可以重用线程,则可以减去新建终止的开销以提升性能。同时,使用线程池的语法比自己新建线程执行线程更加简洁。 Python为我们提供了ThreadPoolExecutor来实现线程池,此线程池默认子线程守护。它的适应场景为突发性大量请求或需要大量线程完成任务,但实际任务处理时间较短。fromtimeimportsleepfun为定义的待运行函数withThreadPoolExecutor(maxworkers5)asexecutor:ansexecutor。map(fun,〔遍历值〕)forresinans:print(res)withThreadPoolExecutor(maxworkers5)asexecutor:list〔遍历值〕ans〔executor。submit(fun,i)foriinlist〕forresinascompleted(ans):print(res。result()) 其中maxworkers为线程池中的线程个数,常用的遍历方法有map和submitascompleted。根据业务场景的不同,若我们需要输出结果按遍历顺序返回,我们就用map方法,若想谁先完成就返回谁,我们就用submitascomplete方法。线程互斥 我们把一个时间段内只允许一个线程使用的资源称为临界资源,对临界资源的访问,必须互斥的进行。互斥,也称间接制约关系。线程互斥指当一个线程访问某临界资源时,另一个想要访问该临界资源的线程必须等待。当前访问临界资源的线程访问结束,释放该资源之后,另一个线程才能去访问临界资源。锁的功能就是实现线程互斥。 我把线程互斥比作厕所包间上大号的过程,因为包间里只有一个坑,所以只允许一个人进行大号。当第一个人要上厕所时,会将门上上锁,这时如果第二个人也想大号,那就必须等第一个人上完,将锁解开后才能进行,在这期间第二个人就只能在门外等着。这个过程与代码中使用锁的原理如出一辙,这里的坑就是临界资源。Python的threading模块引入了锁。threading模块提供了Lock类,它有如下方法加锁和释放锁:acquire():对Lock加锁,其中timeout参数指定加锁多少秒release():释放锁classAccount:definit(self,cardid,balance):封装账户ID、账户余额的两个变量self。cardidcardidself。balancebalancedefwithdraw(account,money):进行加锁lock。acquire()账户余额大于取钱数目ifaccount。balancemoney:吐出钞票print(threading。currentthread()。name取钱成功!吐出钞票:str(money),end)修改余额account。balancemoneyprint(余额为:str(account。balance))else:print(threading。currentthread()。name取钱失败!余额不足)进行解锁lock。release()创建一个账户,银行卡id为8888,存款1000元acctAccount(8888,1000)模拟两个对同一个账户取钱在主线程中创建一把锁lockthreading。Lock()threading。Thread(name窗口A,targetwithdraw,args(acct,800))。start()threading。Thread(name窗口B,targetwithdraw,args(acct,800))。start()Lock与Rlock的区别区别一:Lock被称为原始锁,一个线程只能请求一次;RLock被称为重入锁,可以被一个线程请求多次,即锁中可以嵌套锁。importthreadingdefmain():lock。acquire()print(第一道锁)lock。acquire()print(第二道锁)lock。release()lock。release()ifnamemain:lockthreading。Lock()main() 我们会发现这个程序只会打印第一道锁,而且程序既没有终止,也没有继续运行。这是因为Lock锁在同一线程内第一次加锁之后还没有释放时,就进行了第二次acquire请求,导致无法执行release,所以锁永远无法释放,这就是死锁。如果我们使用RLock就能正常运行,不会发生死锁的状态。区别二:当Lock处于锁定状态时,不属于特定线程,可在另一个线程中进行解锁释放;而RLock只有当前线程才能释放本线程上的锁,不可由其他线程进行释放,所以在使用RLock时,acquire与release必须成对出现,即解铃还须系铃人。importthreadingdefmain():lock。release()print(在子线程解锁后打印)ifnamemain:lockthreading。Lock()lock。acquire()tthreading。Thread(targetmain)t。start() 在主线程中定义Lock锁,然后上锁,再创建一个子线程t运行main函数释放锁,结果正常输出,说明主线程上的锁,可由子线程解锁。 如果把上面的锁改为RLock则报错。在实际中设计程序时,我们会将每个功能分别封装成一个函数,每个函数中都可能会有临界区域,所以就需要用到RLock。importthreadingimporttimedeffun1():print(开始)time。sleep(1)lock。acquire()print(第一道锁)fun2()lock。release()deffun2():lock。acquire()print(第二道锁)lock。release()ifnamemain:lockthreading。RLock()t1threading。Thread(targetfun1)t2threading。Thread(targetfun1)t1。start()t2。start() 一句话总结就是Lock不能套娃,RLock可以套娃;Lock可以由其他线程中的锁进行操作,RLock只能由本线程进行操作。 以上是今天分享的一部分内容。之前我整理了最新的javapython爬虫等编程学习资料视频,关注我,评论666免费分享给大家!