線程
1.1.1 多線程——threading
python的thread模塊是比較底層的模塊秀仲,python的threading模塊是對(duì)thread做了一些包裝的凝赛,可以更加方便的被使用
1.1.2 使用threading模塊
單線程執(zhí)行
importtime
defsaySorry():
print("親愛的邑闺,我錯(cuò)了,我能吃飯了嗎前计?")
time.sleep(1)
if__name__ =="__main__":
foriinrange(5):
saySorry()
多線程執(zhí)行
importthreading
importtime
defsaySorry():
print("親愛的潜秋,我錯(cuò)了,我能吃飯了嗎疾捍?")
time.sleep(1)
if__name__ =="__main__":
foriinrange(5):
t = threading.Thread(target=saySorry)
t.start()#啟動(dòng)線程奈辰,即讓線程開始執(zhí)行
說(shuō)明
1.可以明顯看出使用了多線程并發(fā)的操作,花費(fèi)時(shí)間要短很多
2.創(chuàng)建好的線程拾氓,需要調(diào)用start()方法來(lái)啟動(dòng)
1.1.3 主線程會(huì)等待所有子線程結(jié)束后才結(jié)束
importthreading
fromtimeimportsleep,ctime
defsing():
foriinrange(3):
print("正在唱歌...%d"%i)
sleep(1)
defdance():
foriinrange(3):
print("正在跳舞...%d"%i)
sleep(1)
if__name__ =='__main__':
print('---開始---:%s'%ctime())
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start()
t2.start()
#sleep(5) #屏蔽此行代碼冯挎,試試看,程序是否會(huì)立馬結(jié)束?
print('---結(jié)束---:%s'%ctime())
1.1.4 查看線程數(shù)量
importthreading
fromtimeimportsleep,ctime
defsing():
foriinrange(3):
print("正在唱歌...%d"%i)
sleep(1)
defdance():
foriinrange(3):
print("正在跳舞...%d"%i)
sleep(1)
if__name__ =='__main__':
print('---開始---:%s'%ctime())
t1 = threading.Thread(target=sing)
t2 = threading.Thread(target=dance)
t1.start()
t2.start()
whileTrue:
length = len(threading.enumerate())
print('當(dāng)前運(yùn)行的線程數(shù)為:%d'%length)
iflength<=1:
break
sleep(0.5)
1.2 threating注意點(diǎn)
1.2.1 線程執(zhí)行代碼的封裝
通過(guò)上一小節(jié)房官,能夠看出趾徽,通過(guò)使用threading模塊能完成多任務(wù)的程序開發(fā),為了讓每個(gè)線程的封裝性更完美翰守,所以使用threading模塊時(shí)孵奶,往往會(huì)定義一個(gè)新的子類class,只要繼承threading.Thread就可以了蜡峰,然后重寫run方法
示例如下:
importthreading
importtime
classMyThread(threading.Thread):
defrun(self):
foriinrange(3):
time.sleep(1)
msg ="I'm "+self.name+' @ '+str(i)#name屬性中保存的是當(dāng)前線程的名字
print(msg)
if__name__ =='__main__':
t = MyThread()
t.start()
說(shuō)明
·python的threading.Thread類有一個(gè)run方法了袁,用于定義線程的功能函數(shù),可以在自己的線程類中覆蓋該方法湿颅。而創(chuàng)建自己的線程實(shí)例后载绿,通過(guò)Thread類的start方法,可以啟動(dòng)該線程油航,交給python虛擬機(jī)進(jìn)行調(diào)度崭庸,當(dāng)該線程獲得執(zhí)行的機(jī)會(huì)時(shí),就會(huì)調(diào)用run方法執(zhí)行線程谊囚。
1.2.1 線程的執(zhí)行順序
#coding=utf-8
importthreading
importtime
classMyThread(threading.Thread):
defrun(self):
foriinrange(3):
time.sleep(1)
msg ="I'm "+self.name+' @ '+str(i)
print(msg)
deftest():
foriinrange(5):
t = MyThread()
t.start()
if__name__ =='__main__':
test()
執(zhí)行結(jié)果:(運(yùn)行的結(jié)果可能不一樣怕享,但是大體是一致的)
I'm Thread-1 @ 0
I'm Thread-2 @ 0
I'm Thread-5 @ 0
I'm Thread-3 @ 0
I'm Thread-4 @ 0
I'm Thread-3 @ 1
I'm Thread-4 @ 1
I'm Thread-5 @ 1
I'm Thread-1 @ 1
I'm Thread-2 @ 1
I'm Thread-4 @ 2
I'm Thread-5 @ 2
I'm Thread-2 @ 2
I'm Thread-1 @ 2
I'm Thread-3 @ 2
說(shuō)明
從代碼和執(zhí)行結(jié)果我們可以看出,多線程程序的執(zhí)行順序是不確定的镰踏。當(dāng)執(zhí)行到sleep語(yǔ)句時(shí)函筋,線程將被阻塞(Blocked),到sleep結(jié)束后奠伪,線程進(jìn)入就緒(Runnable)狀態(tài)跌帐,等待調(diào)度。而線程調(diào)度將自行選擇一個(gè)線程執(zhí)行绊率。上面的代碼中只能保證每個(gè)線程都運(yùn)行完整個(gè)run函數(shù)含末,但是線程的啟動(dòng)順序、run函數(shù)中每次循環(huán)的執(zhí)行順序都不能確定即舌。
總結(jié)
1.每個(gè)線程一定會(huì)有一個(gè)名字佣盒,盡管上面的例子中沒(méi)有指定線程對(duì)象的name,但是python會(huì)自動(dòng)為線程指定一個(gè)名字顽聂。
2.當(dāng)線程的run()方法結(jié)束時(shí)該線程完成肥惭。
3.無(wú)法控制線程調(diào)度程序,但可以通過(guò)別的方式來(lái)影響線程調(diào)度的方式紊搪。
4.線程的幾種狀態(tài)
1.3 多線程-共享全局變量
fromthreadingimportThread
importtime
g_num =100
defwork1():
globalg_num
foriinrange(3):
g_num +=1
print("----in work1, g_num is %d---"%g_num)
defwork2():
globalg_num
print("----in work2, g_num is %d---"%g_num)
print("---線程創(chuàng)建之前g_num is %d---"%g_num)
t1 = Thread(target=work1)
t1.start()
#延時(shí)一會(huì)蜜葱,保證t1線程中的事情做完
time.sleep(1)
t2 = Thread(target=work2)
t2.start()
運(yùn)行結(jié)果:
---線程創(chuàng)建之前g_numis100---
----inwork1, g_numis103---
----inwork2, g_numis103---
列表當(dāng)做實(shí)參傳遞到線程中
fromthreadingimportThread
importtime
defwork1(nums):
nums.append(44)
print("----in work1---",nums)
defwork2(nums):
#延時(shí)一會(huì),保證t1線程中的事情做完
time.sleep(1)
print("----in work2---",nums)
g_nums = [11,22,33]
t1 = Thread(target=work1, args=(g_nums,))
t1.start()
t2 = Thread(target=work2, args=(g_nums,))
t2.start()
運(yùn)行結(jié)果:
----inwork1--- [11,22,33,44]
----inwork2--- [11,22,33,44]
總結(jié):
·在一個(gè)進(jìn)程內(nèi)的所有線程共享全局變量耀石,能夠在不適用其他方式的前提下完成多線程之間的數(shù)據(jù)共享(這點(diǎn)要比多進(jìn)程要好)
·缺點(diǎn)就是牵囤,線程是對(duì)全局變量隨意遂改可能造成多線程之間對(duì)全局變量的混亂(即線程非安全)
1.4 進(jìn)程vs 線程
1.4.1 功能
·進(jìn)程,能夠完成多任務(wù),比如在一臺(tái)電腦上能夠同時(shí)運(yùn)行多個(gè)QQ
·線程揭鳞,能夠完成多任務(wù)炕贵,比如一個(gè)QQ中的多個(gè)聊天窗口
1.4.2 定義的不同
·進(jìn)程是系統(tǒng)進(jìn)行資源分配和調(diào)度的一個(gè)獨(dú)立單位.
·線程是進(jìn)程的一個(gè)實(shí)體,是CPU調(diào)度和分派的基本單位,它是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位.線程自己基本上不擁有系統(tǒng)資源,只擁有一點(diǎn)在運(yùn)行中必不可少的資源(如程序計(jì)數(shù)器,一組寄存器和棧),但是它可與同屬一個(gè)進(jìn)程的其他的線程共享進(jìn)程所擁有的全部資源.
1.4.3 區(qū)別
·一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程.
·線程的劃分尺度小于進(jìn)程(資源比進(jìn)程少),使得多線程程序的并發(fā)性高野崇。
·進(jìn)程在執(zhí)行過(guò)程中擁有獨(dú)立的內(nèi)存單元称开,而多個(gè)線程共享內(nèi)存,從而極大地提高了程序的運(yùn)行效率
·線程不能夠獨(dú)立執(zhí)行乓梨,必須依存在進(jìn)程中
1.4.4 優(yōu)缺點(diǎn)
線程和進(jìn)程在使用上各有優(yōu)缺點(diǎn):線程執(zhí)行開銷小鳖轰,但不利于資源的管理和保護(hù);而進(jìn)程正相反扶镀。