python多线程(python多线程实例详解)

多线程和多进程是什么自行google补脑

对于python多线程的理解,我花了很长时刻,查找的大部份文章都不行通俗易懂。所以,这里力求用简略的比如,让你对多线程有个初步的认识。

单线程

在好些年前的MS-DOS年代,操作系统处理问题都是单任务的,我想做听音乐和看电影两件事儿,那么必定要先排一下次序。

(好吧!咱们不纠结在DOS年代是否有听音乐和看影的应用。^_^)

fromtimeimportctime,sleepdefmusic():foriinrange(2):print”Iwaslisteningtomusic.%s”%ctime()

sleep(1)defmove():foriinrange(2):print”Iwasatthemovies!%s”%ctime()

sleep(5)if__name__==’__main__’:

music()

move()print”allover%s”%ctime()

咱们先听了一首音乐,经过for循环来控制音乐的播映了两次,每首音乐播映需求1秒钟,sleep()来控制音乐播映的时长。接着咱们又看了一场电影,

每一场电影需求5秒钟,由于太好看了,所以我也经过for循环看两遍。在整个休闲娱乐活动完毕后,我经过

print”allover%s”%ctime()

看了一下当时时刻,差不多该睡觉了。

运转成果:

>>===========================RESTART================================

>>>Iwaslisteningtomusic.ThuApr1710:47:082014Iwaslisteningtomusic.ThuApr1710:47:092014Iwasatthemovies!ThuApr1710:47:102014Iwasatthemovies!ThuApr1710:47:152014alloverThuApr1710:47:202014

其实,music()和move()更应该被看作是音乐和视频播映器,至于要播映什么歌曲和视频应该由咱们运用时决议。所以,咱们对上面代码做了改造:

#coding=utf-8importthreadingfromtimeimportctime,sleepdefmusic(func):foriinrange(2):print”Iwaslisteningto%s.%s”%(func,ctime())

sleep(1)defmove(func):foriinrange(2):print”Iwasatthe%s!%s”%(func,ctime())

sleep(5)if__name__==’__main__’:

music(u’爱情生意’)

move(u’阿凡达’)print”allover%s”%ctime()

对music()和move()进行了传参处理。体会我国经典歌曲和欧美大片文明。

运转成果:

>>>========================RESTART================================

>>>Iwaslisteningto爱情生意.ThuApr1711:48:592014Iwaslisteningto爱情生意.ThuApr1711:49:002014Iwasatthe阿凡达!ThuApr1711:49:012014Iwasatthe阿凡达!ThuApr1711:49:062014alloverThuApr1711:49:112014

多线程

科技在发展,年代在进步,咱们的CPU也越来越快,CPU抱怨,P大点事儿占了我必定的时刻,其实我一起干多个活都没问题的;所以,操作系统就进入了多任务年代。咱们听着音乐吃着火锅的不在是梦想。

python提供了两个模块来完结多线程thread和threading,thread有一些缺点,在threading得到了弥补,为了不糟蹋你和时刻,所以咱们直接学习threading就可以了。

持续对上面的比如进行改造,引入threadring来一起播映音乐和视频:

#coding=utf-8importthreadingfromtimeimportctime,sleepdefmusic(func):foriinrange(2):print”Iwaslisteningto%s.%s”%(func,ctime())

sleep(1)defmove(func):foriinrange(2):print”Iwasatthe%s!%s”%(func,ctime())

sleep(5)

threads=[]

t1=threading.Thread(target=music,args=(u’爱情生意’,))

threads.append(t1)

t2=threading.Thread(target=move,args=(u’阿凡达’,))

threads.append(t2)if__name__==’__main__’:fortinthreads:

t.setDaemon(True)

t.start()print”allover%s”%ctime()

importthreading

首先导入threading模块,这是运用多线程的前提。

threads=[]

t1=threading.Thread(target=music,args=(u’爱情生意’,))

threads.append(t1)

创立了threads数组,创立线程t1,运用threading.Thread()办法,在这个办法中调用music办法target=music,args办法对music进行传参。把创建好的线程t1装到threads数组中。

接着以同样的办法创立线程t2,并把t2也装到threads数组。

fortinthreads:

t.setDaemon(True)

t.start()

最终经过for循环遍历数组。(数组被装载了t1和t2两个线程)

setDaemon()

setDaemon(True)将线程声明为看护线程,有必要在start()办法调用之前设置,如果不设置为看护线程程序会被无限挂起。子线程发动后,父线程也持续履行下去,当父线程履行完最终一条句子print”allover%s”%ctime()后,没有等候子线程,直接就退出了,一起子线程也一同完毕。

start()

开始线程活动。

运转成果:

>>>=========================RESTART================================

>>>Iwaslisteningto爱情生意.ThuApr1712:51:452014Iwasatthe阿凡达!ThuApr1712:51:452014alloverThuApr1712:51:452014

从履行成果来看,子线程(muisc、move)和主线程(print”allover%s”%ctime())都是同一时刻发动,但由于主线程履行完完毕,所以导致子线程也停止。

持续调整程序:

…if__name__==’__main__’:fortinthreads:

t.setDaemon(True)

t.start()fortinthreads:

t.join()print”allover%s”%ctime()

咱们只对上面的程序加了个join()办法,用于等候线程停止。join()的作用是,在子线程完结运转之前,这个子线程的父线程将一直被堵塞。

留意:join()办法的位置是在for循环外的,也就是说有必要等候for循环里的两个进程都完毕后,才去履行主进程。
图片[1]-python多线程(python多线程实例详解)-小白之家,python多线程实例详解,一、进程与线程关系

一个进程至少包括一个线程。

二、线程基础

1、线程的状况

线程有5种状况,状况转化的过程如下图所示:

2、线程同步(锁)

多线程的优势在于能够一起运转多个使命(至少感觉起来是这样)。但是当线程需求共享数据时,或许存在数据不同步的问题。考虑这样一种状况:一个列表里一切元素都是0,线程”set”从后向前把一切元素改成1,而线程”print”负责从前往后读取列表并打印。那么,或许线程”set”开端改的时分,线程”print”便来打印列表了,输出就成了一半0一半1,这便是数据的不同步。为了防止这种状况,引入了锁的概念。

锁有两种状况——确定和未确定。每当一个线程比如”set”要拜访共享数据时,有必要先取得确定;假如现已有其他线程比如”print”取得确定了,那么就让线程”set”暂停,也便是同步堵塞;比及线程”print”拜访完毕,开释锁今后,再让线程”set”持续。经过这样的处理,打印列表时要么悉数输出0,要么悉数输出1,不会再出现一半0一半1的尴尬局面。

线程与锁的交互如下图所示:

线程与锁

3、线程通讯(条件变量)

然而还有另外一种尴尬的状况:列表并不是一开端就有的;而是经过线程”create”创立的。假如”set”或许”print”在”create”还没有运转的时分就拜访列表,将会出现一个反常。运用锁能够处理这个问题,但是”set”和”print”将需求一个无限循环——他们不知道”create”什么时分会运转,让”create”在运转后告诉”set”和”print”显然是一个更好的处理方案。所以,引入了条件变量。

条件变量允许线程比如”set”和”print”在条件不满足的时分(列表为None时)等候,比及条件满足的时分(列表现已创立)发出一个告诉,告诉”set”和”print”条件现已有了,你们该起床干活了;然后”set”和”print”才持续运转。

线程与条件变量的交互如下图所示:

需求条件的线程

创造条件的线程

4、线程运转和堵塞的状况转化

最终看看线程运转和堵塞状况的转化

线程运转与堵塞状况转化

堵塞有三种状况:

同步堵塞(确定池)是指处于竞赛确定的状况,线程恳求确守时将进入这个状况,一旦成功取得确定又康复到运转状况;

等候堵塞(等候池)是指等候其他线程告诉的状况,线程取得条件确定后,调用“等候”将进入这个状况,一旦其他线程发出告诉,线程将进入同步堵塞状况,再次竞赛条件确定;

而其他堵塞是指调用time.sleep()、anotherthread.join()或等候IO时的堵塞,这个状况下线程不会开释已取得的确定。

tips:假如能了解这些内容,接下来的主题将对错常轻松的;并且,这些内容在大部分流行的编程语言里都是相同的。(意思便对错看懂不可>_<嫌作者水平低找他人的教程也要看懂)

三、thread

Python经过两个标准库thread和threading供给对线程的支持。thread供给了初级其他、原始的线程以及一个简略的锁。

#encoding:UTF-8importthreadimporttime#一个用于在线程中履行的函数deffunc():foriinrange(5):print’func’time.sleep(1)#完毕当时线程#这个办法与thread.exit_thread()等价thread.exit()#当func回来时,线程同样会完毕#发动一个线程,线程当即开端运转#这个办法与thread.start_new_thread()等价#第一个参数是办法,第二个参数是办法的参数thread.start_new(func,())#办法没有参数时需求传入空tuple#创立一个锁(LockType,不能直接实例化)#这个办法与thread.allocate_lock()等价lock=thread.allocate()#判断锁是确定状况还是开释状况printlock.locked()#锁通常用于控制对共享资源的拜访count=0#取得锁,成功取得确定后回来True#可选的timeout参数不填时将一向堵塞直到取得确定#不然超时后将回来Falseiflock.acquire():

count+=1#开释锁lock.release()#thread模块供给的线程都将在主线程完毕后一起完毕time.sleep(6)

thread模块供给的其他办法:

thread.interrupt_main():在其他线程中停止主线程。

thread.get_ident():取得一个代表当时线程的魔法数字,常用于从一个字典中取得线程相关的数据。这个数字本身没有任何含义,并且当线程完毕后会被新线程复用。

thread还供给了一个ThreadLocal类用于办理线程相关的数据,名为thread._local,threading中引证了这个类。

因为thread供给的线程功能不多,无法在主线程完毕后持续运转,不供给条件变量等等原因,一般不运用thread模块,这里就不多介绍了。

四、threading

threading基于Java的线程模型设计。锁(Lock)和条件变量(Condition)在Java中是目标的根本行为(每一个目标都自带了锁和条件变量),而在Python中则是独立的目标。PythonThread供给了JavaThread的行为的子集;没有优先级、线程组,线程也不能被中止、暂停、康复、中断。JavaThread中的部分被Python完成了的静态办法在threading中以模块办法的方式供给。

threading模块供给的常用办法:

threading.currentThread():回来当时的线程变量。

threading.enumerate():回来一个包括正在运转的线程的list。正在运转指线程发动后、完毕前,不包括发动前和停止后的线程。

threading.activeCount():回来正在运转的线程数量,与len(threading.enumerate())有相同的结果。

threading模块供给的类:

Thread,Lock,Rlock,Condition,[Bounded]Semaphore,Event,Timer,local.

1、Thread

Thread是线程类,与Java相似,有两种运用办法,直接传入要运转的办法或从Thread承继并掩盖run():

#encoding:UTF-8importthreading#办法1:将要履行的办法作为参数传给Thread的结构办法deffunc():print’func()passedtoThread’t=threading.Thread(target=func)

t.start()#办法2:从Thread承继,并重写run()classMyThread(threading.Thread):defrun(self):print’MyThreadextendedfromThread’t=MyThread()

t.start()

结构办法:

Thread(group=None,target=None,name=None,args=(),kwargs={})

group:线程组,现在还没有完成,库引证中提示有必要是None;

target:要履行的办法;

name:线程名;

args/kwargs:要传入办法的参数。

实例办法:

isAlive():回来线程是否在运转。正在运转指发动后、停止前。

get/setName(name):获取/设置线程名。

is/setDaemon(bool):获取/设置是否看护线程。初始值从创立该线程的线程承继。当没有非看护线程仍在运转时,程序将停止。

start():发动线程。

join([timeout]):堵塞当时上下文环境的线程,直到调用此办法的线程停止或到达指定的timeout(可选参数)。

一个运用join()的比如:

#encoding:UTF-8importthreadingimporttimedefcontext(tJoin):print’inthreadContext.’tJoin.start()#将堵塞tContext直到threadJoin停止。tJoin.join()#tJoin停止后持续履行。print’outthreadContext.’defjoin():print’inthreadJoin.’time.sleep(1)print’outthreadJoin.’#tJoin和tContext分别为两个不同的线程tJoin=threading.Thread(target=join)

tContext=threading.Thread(target=context,args=(tJoin,))

tContext.start()

运转结果:

inthreadContext.inthreadJoin.outthreadJoin.outthreadContext.

2、Lock

Lock(指令锁)是可用的最初级的同步指令。Lock处于确定状况时,不被特定的线程具有。Lock包括两种状况——确定和非确定,以及两个根本的办法。

能够认为Lock有一个确定池,当线程恳求确守时,将线程至于池中,直到取得确定后出池。池中的线程处于状况图中的同步堵塞状况。

结构办法:

Lock()

实例办法:

acquire([timeout]):使线程进入同步堵塞状况,测验取得确定。

release():开释锁。运用前线程有必要已取得确定,不然将抛出反常。

#encoding:UTF-8importthreadingimporttime

data=0lock=threading.Lock()deffunc():globaldataprint’%sacquirelock…’%threading.currentThread().getName()#调用acquire([timeout])时,线程将一向堵塞,#直到取得确定或许直到timeout秒后(timeout参数可选)。#回来是否取得锁。iflock.acquire():print’%sgetthelock.’%threading.currentThread().getName()

data+=1time.sleep(2)print’%sreleaselock…’%threading.currentThread().getName()#调用release()将开释锁。lock.release()

t1=threading.Thread(target=func)

t2=threading.Thread(target=func)

t3=threading.Thread(target=func)

t1.start()

t2.start()

t3.start()

多运转几次,你会看到打印的信息次序并不一致,这就证实了线程在确定池中谁将取得锁运转是由系统调度决定(随机,不确定)

RLock

RLock(可重入锁)是一个能够被同一个线程恳求多次的同步指令。RLock运用了“具有的线程”和“递归等级”的概念,处于确定状况时,RLock被某个线程具有。具有RLock的线程能够再次调用acquire(),开释锁时需求调用release()相同次数。

能够认为RLock包括一个确定池和一个初始值为0的计数器,每次成功调用acquire()/release(),计数器将+1/-1,为0时锁处于未确定状况。

结构办法:

RLock()

实例办法:

acquire([timeout])/release():跟Lock差不多。

#encoding:UTF-8importthreadingimporttime

rlock=threading.RLock()deffunc():#第一次恳求确定print’%sacquirelock…’%threading.currentThread().getName()ifrlock.acquire():print’%sgetthelock.’%threading.currentThread().getName()

time.sleep(2)#第二次恳求确定print’%sacquirelockagain…’%threading.currentThread().getName()ifrlock.acquire():print’%sgetthelock.’%threading.currentThread().getName()

time.sleep(2)#第一次开释锁print’%sreleaselock…’%threading.currentThread().getName()

rlock.release()

time.sleep(2)#第二次开释锁print’%sreleaselock…’%threading.currentThread().getName()

rlock.release()

t1=threading.Thread(target=func)

t2=threading.Thread(target=func)

t3=threading.Thread(target=func)

t1.start()

t2.start()

t3.start()

4、Condition

Condition(条件变量)通常与一个锁相关。需求在多个Contidion中共享一个锁时,能够传递一个Lock/RLock实例给结构办法,不然它将自己生成一个RLock实例。

能够认为,除了Lock带有的确定池外,Condition还包括一个等候池,池中的线程处于状况图中的等候堵塞状况,直到另一个线程调用notify()/notifyAll()告诉;得到告诉后线程进入确定池等候确定。

结构办法:

Condition([lock/rlock])

实例办法:

acquire([timeout])/release():调用相关的锁的相应办法。

wait([timeout]):调用这个办法将使线程进入Condition的等候池等候告诉,并开释锁。运用前线程有必要已取得确定,不然将抛出反常。

notify():调用这个办法将从等候池挑选一个线程并告诉,收到告诉的线程将主动调用acquire()测验取得确定(进入确定池);其他线程仍然在等候池中。调用这个办法不会开释确定。运用前线程有必要已取得确定,不然将抛出反常。

notifyAll():调用这个办法将告诉等候池中一切的线程,这些线程都将进入确定池测验取得确定。调用这个办法不会开释确定。运用前线程有必要已取得确定,不然将抛出反常。

比如是很常见的出产者/顾客模式:

#encoding:UTF-8importthreadingimporttime#产品product=None#条件变量con=threading.Condition()#出产者办法defproduce():globalproductifcon.acquire():whileTrue:ifproductisNone:print’produce…’product=’anything’#告诉顾客,产品现已出产con.notify()#等候告诉con.wait()

time.sleep(2)#顾客办法defconsume():globalproductifcon.acquire():whileTrue:ifproductisnotNone:print’consume…’product=None#告诉出产者,产品现已没了con.notify()#等候告诉con.wait()

time.sleep(2)

t1=threading.Thread(target=produce)

t2=threading.Thread(target=consume)

t2.start()

t1.start()

5、Semaphore/BoundedSemaphore

Semaphore(信号量)是计算机科学史上最古老的同步指令之一。Semaphore办理一个内置的计数器,每当调用acquire()时-1,调用release()时+1。计数器不能小于0;当计数器为0时,acquire()将堵塞线程至同步确定状况,直到其他线程调用release()。

基于这个特色,Semaphore经常用来同步一些有“访客上限”的目标,比如连接池。

BoundedSemaphore与Semaphore的唯一差异在于前者将在调用release()时查看计数器的值是否超过了计数器的初始值,假如超过了将抛出一个反常。

结构办法:

Semaphore(value=1):value是计数器的初始值。

实例办法:

acquire([timeout]):恳求Semaphore。假如计数器为0,将堵塞线程至同步堵塞状况;不然将计数器-1并当即回来。

release():开释Semaphore,将计数器+1,假如运用BoundedSemaphore,还将进行开释次数查看。release()办法不查看线程是否已取得Semaphore。

#encoding:UTF-8importthreadingimporttime#计数器初值为2semaphore=threading.Semaphore(2)deffunc():#恳求Semaphore,成功后计数器-1;计数器为0时堵塞print’%sacquiresemaphore…’%threading.currentThread().getName()ifsemaphore.acquire():print’%sgetsemaphore’%threading.currentThread().getName()

time.sleep(4)#开释Semaphore,计数器+1print’%sreleasesemaphore’%threading.currentThread().getName()

semaphore.release()

t1=threading.Thread(target=func)

t2=threading.Thread(target=func)

t3=threading.Thread(target=func)

t4=threading.Thread(target=func)

t1.start()

t2.start()

t3.start()

t4.start()

time.sleep(2)#没有取得semaphore的主线程也能够调用release#若运用BoundedSemaphore,t4开释semaphore时将抛出反常print’MainThreadreleasesemaphorewithoutacquire’semaphore.release()

6、Event

Event(事情)是最简略的线程通讯机制之一:一个线程告诉事情,其他线程等候事情。Event内置了一个初始为False的标志,当调用set()时设为True,调用clear()时重置为False。wait()将堵塞线程至等候堵塞状况。

Event其实便是一个简化版的Condition。Event没有锁,无法使线程进入同步堵塞状况。

© 版权声明
THE END
喜欢就支持一下吧
点赞0赞赏 分享
评论 抢沙发

请登录后发表评论