1、time模塊
簡(jiǎn)單介紹
常用的一種獲取當(dāng)前時(shí)間以及時(shí)間格式化的模塊,模塊名稱(chēng):time
time模塊在Python原生安裝中就存在所以不需要進(jìn)行任何安裝操作,直接使用即可。
導(dǎo)入方式:import time
常用方法

[注]常用時(shí)間函數(shù)
獲取當(dāng)前時(shí)間的時(shí)間戳? ??time.time()?
獲取當(dāng)前時(shí)間:time.ctime()不傳參數(shù)【可以傳入一個(gè)時(shí)間戳,返回格式化后的時(shí)間】
獲取當(dāng)前時(shí)間的時(shí)間元祖:time.localtime()????接受時(shí)間戳

時(shí)間格式

時(shí)間元祖

2、Python 多進(jìn)程多線(xiàn)程
基本概念
大家都知道計(jì)算機(jī)由CPU、內(nèi)存、輸入設(shè)備、輸出設(shè)備組成。CPU主要是負(fù)責(zé)計(jì)算。而操作系統(tǒng)是計(jì)算機(jī)管理者,負(fù)責(zé)任務(wù)的調(diào)度、資源的分配和管理,統(tǒng)領(lǐng)整個(gè)計(jì)算機(jī)硬件;應(yīng)用程序側(cè)是具有某種功能的程序,程序是運(yùn)行于操作系統(tǒng)之上的。
進(jìn)程是能擁有資源和獨(dú)立運(yùn)行的最小單位,也是程序執(zhí)行的最小單位。任務(wù)調(diào)度采用的是時(shí)間片輪轉(zhuǎn)的搶占式調(diào)度方式,而進(jìn)程是任務(wù)調(diào)度的最小單位,每個(gè)進(jìn)程有各自獨(dú)立的一塊內(nèi)存,使得各個(gè)進(jìn)程之間內(nèi)存地址相互隔離,進(jìn)程之間的切換開(kāi)銷(xiāo)很大。我們每次執(zhí)行py文件結(jié)束后會(huì)輸出進(jìn)程結(jié)束
線(xiàn)程是程序執(zhí)行中一個(gè)單一的順序控制流程,是程序執(zhí)行流的最小單元,是處理器調(diào)度和分派的基本單位.一個(gè)進(jìn)程可以有一個(gè)或多個(gè)線(xiàn)程,各個(gè)線(xiàn)程之間共享程序的內(nèi)存空間(也就是所在進(jìn)程的內(nèi)存空間)。一個(gè)標(biāo)準(zhǔn)的線(xiàn)程由線(xiàn)程ID、當(dāng)前指令指針(PC)、寄存器和堆棧組成。而進(jìn)程由內(nèi)存空間(代碼、數(shù)據(jù)、進(jìn)程空間、打開(kāi)的文件)和一個(gè)或多個(gè)線(xiàn)程組成。
進(jìn)程和線(xiàn)程的區(qū)別:
(1)線(xiàn)程是程序執(zhí)行的最小單位,而進(jìn)程是操作系統(tǒng)分配資源的最小單位;
(2)一個(gè)進(jìn)程由一個(gè)或多個(gè)線(xiàn)程組成,線(xiàn)程是一個(gè)進(jìn)程中代碼的不同執(zhí)行路線(xiàn);
(3)進(jìn)程之間相互獨(dú)立,但同一進(jìn)程下的各個(gè)線(xiàn)程之間共享程序的內(nèi)存空間(包括代碼段、數(shù)據(jù)集、堆等)及一些進(jìn)程級(jí)的資源(如打開(kāi)文件和信號(hào)),某進(jìn)程內(nèi)的線(xiàn)程在其它進(jìn)程不可見(jiàn);
(4)調(diào)度和切換:線(xiàn)程上下文切換比進(jìn)程上下文切換要快得多。
總之,線(xiàn)程和進(jìn)程都是一種抽象的概念,線(xiàn)程是一種比進(jìn)程更小的抽象,線(xiàn)程和進(jìn)程都可用于實(shí)現(xiàn)并發(fā)。


單線(xiàn)程:在單線(xiàn)程中,當(dāng)處理器需要處理多個(gè)任務(wù)時(shí),必須對(duì)這些任務(wù)安排執(zhí)行順序,并按照這個(gè)順序來(lái)執(zhí)行任務(wù)。假如我們創(chuàng)建了兩個(gè)任務(wù):聽(tīng)音樂(lè)(music)和看電影(movie)。在單線(xiàn)程中,我們只能按先后順序來(lái)執(zhí)行這兩個(gè)任務(wù)

多線(xiàn)程
我們知道大部分操作系統(tǒng)(如Windows、Linux)的任務(wù)調(diào)度是采用時(shí)間片輪轉(zhuǎn)的搶占式調(diào)度方式,也就是說(shuō)一個(gè)任務(wù)執(zhí)行一小段時(shí)間后強(qiáng)制暫停去執(zhí)行下一個(gè)任務(wù),每個(gè)任務(wù)輪流執(zhí)行。多線(xiàn)程指的是多個(gè)任務(wù)同時(shí)進(jìn)行。多線(xiàn)程一般用于IO型密集場(chǎng)景。在計(jì)算密集型的場(chǎng)景下,線(xiàn)程之間切換也是有消耗的。而且Python的GIL使得統(tǒng)一時(shí)間內(nèi)只能有一個(gè)線(xiàn)程運(yùn)行,因此若是想提高CPU利用率建議用多進(jìn)程
3、Python多線(xiàn)程模塊之thread和Threading模塊
推薦使用threading模塊,主要原因是, thread不支持守護(hù)線(xiàn)程。當(dāng)主線(xiàn)程退出時(shí),所有的子線(xiàn)程不管它們是否還在工作,都會(huì)被強(qiáng)行退出。有時(shí)我們并不希望發(fā)生這種行為,這時(shí)就引入了守護(hù)線(xiàn)程的概念。threading模塊支持守護(hù)線(xiàn)程
threading使用簡(jiǎn)介-參數(shù)詳解
threading.Thread(self, group=None, target=None,name=None, args=(), kwargs={})
參數(shù)group是預(yù)留的,用于將來(lái)擴(kuò)展;
參數(shù)target是一個(gè)可調(diào)用對(duì)象(也稱(chēng)為活動(dòng)[activity]),在線(xiàn)程啟動(dòng)后執(zhí)行;可調(diào)用對(duì)象比如傳入方法名
參數(shù)name是線(xiàn)程的名字。默認(rèn)值為“Thread-N“,N是一個(gè)數(shù)字。
參數(shù)args和kwargs分別表示調(diào)用target時(shí)的參數(shù)列表和關(guān)鍵字參數(shù)。比如調(diào)用方法的時(shí)候需要傳入方法需要的參數(shù)
threading模塊之Thread類(lèi)常用方法與屬性:
Thread.getName()/ Thread.name? 用于獲取線(xiàn)程的名稱(chēng),有返回值,需要print打印出來(lái)
Thread.setName()?? 用于設(shè)置線(xiàn)程的名稱(chēng),沒(méi)有返回值,print打印出來(lái)事None
Thread.ident ?獲取線(xiàn)程的標(biāo)識(shí)符。線(xiàn)程標(biāo)識(shí)符是一個(gè)非零整數(shù),只有在調(diào)用了start()方法之后該屬性才有效,否則它只返回None。
Thread.is_alive()? /Thread.isAlive? 判斷線(xiàn)程是否是激活的(alive)。從調(diào)用start()方法啟動(dòng)線(xiàn)程,到run()方法執(zhí)行完畢或遇到未處理異常而中斷這段時(shí)間內(nèi),線(xiàn)程是激活的。
Thread.join([timeout]) 調(diào)用Thread.join將會(huì)使主線(xiàn)程堵塞,直到被調(diào)用線(xiàn)程運(yùn)行結(jié)束或超時(shí)。參數(shù)timeout是一個(gè)數(shù)值類(lèi)型,表示超時(shí)時(shí)間,如果未提供該參數(shù),那么主調(diào)線(xiàn)程將一直堵塞到被調(diào)線(xiàn)程結(jié)束
代碼如下
import threading
import time
def music(n):
for iin range(n):
print('this is music method;number is %s'%i)
time.sleep(1)
def dance(n):
for iin range(n):
print('this is dance method;number is %s'%i)
print('the thread name is %s'%threading.Thread.getName(t2))
print('time is %s'%time.ctime())
time.sleep(1)
t1 = threading.Thread(target=music,name='music', args=(20,))
t2 = threading.Thread(target=dance,name='dance', args=(10,))
t2.setName('dance02')
if __name__ =='__main__':
t1.start()
# t1.join()
? ? t2.start()
# time.sleep(20)
print(threading.Thread.is_alive(t1))#rue
print(threading.Thread.is_alive(t2))#False
join方法詳解
Thread.join([timeout]) 調(diào)用Thread.join將會(huì)使主線(xiàn)程堵塞,直到被調(diào)用線(xiàn)程運(yùn)行結(jié)束或超時(shí)。參數(shù)timeout是一個(gè)數(shù)值類(lèi)型,表示超時(shí)時(shí)間,如果未提供該參數(shù),那么主調(diào)線(xiàn)程將一直堵塞到被調(diào)線(xiàn)程結(jié)束
1.什么是子線(xiàn)程??包含在 threading.Thread中,里面均視為子線(xiàn)程。
2.什么是主線(xiàn)程??除了“不包含在Thread里面的程序”,UI界面和Main函數(shù)均為主線(xiàn)程,均可視為主線(xiàn)程
【注】Thread 是threading模塊中最重要的類(lèi)之一,可以使用它來(lái)創(chuàng)建線(xiàn)程。有兩種方式來(lái)創(chuàng)建線(xiàn)程:一種是通過(guò)繼承Thread類(lèi),重寫(xiě)它的run方法;另一種是創(chuàng)建一個(gè)threading.Thread對(duì)象,在它的初始化函數(shù)(__init__)中將可調(diào)用對(duì)象作為參數(shù)傳入。
守護(hù)線(xiàn)程:子線(xiàn)程設(shè)置成主線(xiàn)程的守護(hù)線(xiàn)程,那么主線(xiàn)程結(jié)束的時(shí)候會(huì)殺死子線(xiàn)程

GIL全局鎖
首先多線(xiàn)程目的是為了提升代碼執(zhí)行效率,但是同時(shí)會(huì)引出一個(gè)問(wèn)題。因?yàn)槎嗑€(xiàn)程之間是共享內(nèi)存和數(shù)據(jù)的。所以要解決的一個(gè)問(wèn)題是保證線(xiàn)程間數(shù)據(jù)一致性和狀態(tài)同步。解決多線(xiàn)程之間數(shù)據(jù)完整性和狀態(tài)同步的最簡(jiǎn)單方法自然就是加鎖。于是有了GIL這把超級(jí)大鎖GIL是全局解釋鎖,在Python解釋器解釋執(zhí)行任何 Python 代碼時(shí),都需要先獲得這把鎖才行,GIL也不可避免的帶來(lái)了一定的性能損失,在遇到 I/O 操作時(shí)會(huì)釋放這把鎖,如果是純計(jì)算的程序,沒(méi)有 I/O 操作,解釋器會(huì)每隔 100 次操作就釋放這把鎖,讓別的線(xiàn)程有機(jī)會(huì)執(zhí)行
GIL全局鎖帶來(lái)的問(wèn)題:當(dāng)CPU有多個(gè)核心的時(shí)候,問(wèn)題就來(lái)了。從release GIL到acquireGIL之間幾乎是沒(méi)有間隙的。所以當(dāng)其他在其他核心上的線(xiàn)程被喚醒時(shí),大部分情況下主線(xiàn)程已經(jīng)又再一次獲取到GIL了。這個(gè)時(shí)候被喚醒執(zhí)行的線(xiàn)程只能白白的浪費(fèi)CPU時(shí)間,看著另一個(gè)線(xiàn)程拿著GIL歡快的執(zhí)行著。然后達(dá)到切換時(shí)間后進(jìn)入待調(diào)度狀態(tài),再被喚醒,再等待,以此往復(fù)惡性循環(huán)。GIL的存在導(dǎo)致多線(xiàn)程無(wú)法很好的利用多核CPU的并發(fā)處理能力。

GIL全局鎖是無(wú)法保證數(shù)據(jù)統(tǒng)一性的,在程序中如果需要保證兩個(gè)子線(xiàn)程之間互不影響,需要加鎖。
4、Python多進(jìn)程模塊multiprocessing
multiprocessing庫(kù)的出現(xiàn)很大程度上是為了彌補(bǔ)thread庫(kù)因?yàn)镚IL低效的缺陷。它完整的復(fù)制了一套thread所提供的接口方便遷移。唯一的不同就是它使用了多進(jìn)程而不是多線(xiàn)程。每個(gè)進(jìn)程有自己的獨(dú)立的GIL,完全并行,無(wú)GIL的限制(進(jìn)程中包括線(xiàn)程),可充分利用多cpu多核的環(huán)境,因此也不會(huì)出現(xiàn)進(jìn)程之間的GIL爭(zhēng)搶
簡(jiǎn)單介紹
python多進(jìn)程并發(fā),模塊名稱(chēng):multiprocessing
python中的多線(xiàn)程其實(shí)并不是真正的多線(xiàn)程,如果想要充分地使用多核CPU的資源,在python中大部分情況需要使用多進(jìn)程
借助這個(gè)包,可以輕松完成從單進(jìn)程到并發(fā)執(zhí)行的轉(zhuǎn)換
導(dǎo)入方式: import multiprocessing
Multiprocessing使用簡(jiǎn)介
multiprocessing包是Python中的多進(jìn)程管理包。與threading.Thread類(lèi)似,它可以利用multiprocessing.Process對(duì)象來(lái)創(chuàng)建一個(gè)進(jìn)程。該P(yáng)rocess對(duì)象與Thread對(duì)象的用法相同,也有start(), run(), join()等方法
此外multiprocessing包中也有Lock/Event/Semaphore/Condition類(lèi) (這些對(duì)象可以像多線(xiàn)程那樣,通過(guò)參數(shù)傳遞給各個(gè)進(jìn)程),用以同步進(jìn)程,其用法與threading包中的Thread類(lèi)一致。所以,multiprocessing的很大一部份與threading使用同一套API,只不過(guò)換到了多進(jìn)程的情境
multiprocessing提供了threading包中沒(méi)有的IPC(比如Pipe和Queue),效率上更高。應(yīng)優(yōu)先考慮Pipe和Queue,避免使用Lock/Event/Semaphore/Condition等同步方式 (因?yàn)樗鼈冋紦?jù)的不是用戶(hù)進(jìn)程的資源,而是線(xiàn)程)
進(jìn)程創(chuàng)建
創(chuàng)建進(jìn)程的類(lèi):Process()

【注】target是可調(diào)用對(duì)象,比如方法名,不能寫(xiě)成字符串
代碼如下:
import time
import multiprocessing
class FunWork(multiprocessing.Process):
????def __init__(self):
????????multiprocessing.Process.__init__(self)
????????print('begin')
????def run(self):
????????for iin range(100):
????????time.sleep(1)
????????print('end %s'%i)
if __name__ =='__main__':
# 方式1,實(shí)例化multiprocessing的Process類(lèi),將可調(diào)用對(duì)象傳進(jìn)去
p1 = multiprocessing.Process(target=work)
p2 = multiprocessing.Process(target=work)
p1.start()
p2.start()
print(n)# 0
# 方式2:定義類(lèi),繼承Process.
# 注意要重寫(xiě)run方法,在init初始化方法中調(diào)用父類(lèi)的初始化方法 ,在調(diào)用start方法時(shí)候就會(huì)自定調(diào)用 重寫(xiě)后的run方法
pp1 = FunWork()
pp2 = FunWork()
pp1.start()
pp2.start()
方法:is_alive()、join([timeout])、run()、start() 。其中,Process以start()啟動(dòng)某個(gè)進(jìn)程
pool進(jìn)程池
Pool可以提供指定數(shù)量的進(jìn)程,供用戶(hù)調(diào)用,當(dāng)有新的請(qǐng)求提交到pool中時(shí),如果池還沒(méi)有滿(mǎn),那么就會(huì)創(chuàng)建一個(gè)新的進(jìn)程用來(lái)執(zhí)行該請(qǐng)求;但如果池中的進(jìn)程數(shù)已經(jīng)達(dá)到規(guī)定最大值,那么該請(qǐng)求就會(huì)等待,直到池中有進(jìn)程結(jié)束,才會(huì)創(chuàng)建新的進(jìn)程來(lái)執(zhí)行它
進(jìn)程池方法
apply(func[, args[, kwds]]): 阻塞的執(zhí)行,比如創(chuàng)建一個(gè)有3個(gè)線(xiàn)程的線(xiàn)程池,當(dāng)執(zhí)行時(shí)是創(chuàng)建完一個(gè),執(zhí)行完函數(shù)再創(chuàng)建另一個(gè),變成一個(gè)線(xiàn)性的執(zhí)行.
apply_async(func[, args[, kwds[, callback]]]) : 它是非阻塞執(zhí)行,同時(shí)創(chuàng)建3個(gè)線(xiàn)程的線(xiàn)城池,同時(shí)執(zhí)行,只要有一個(gè)執(zhí)行完立刻放回池子待下一個(gè)執(zhí)行,并行的執(zhí)行 .
close(): 關(guān)閉pool,使其不在接受新的任務(wù)。?
terminate() : 結(jié)束工作進(jìn)程,不在處理未完成的任務(wù)。?
join():主進(jìn)程阻塞,等待子進(jìn)程的退出,join方法要在close或terminate之后使用

鎖

進(jìn)程間通信
進(jìn)程彼此之間互相隔離,要實(shí)現(xiàn)進(jìn)程間通信(IPC),multiprocessing模塊支持兩種形式:隊(duì)列Queue和管道Pipe,這兩種方式都是使用消息傳遞的
Queue隊(duì)列
Queue([maxsize]):創(chuàng)建共享的進(jìn)程隊(duì)列,Queue是多進(jìn)程安全的隊(duì)列,可以使用Queue實(shí)現(xiàn)多進(jìn)程之間的數(shù)據(jù)傳遞。? ?參數(shù)介紹:maxsize是隊(duì)列中允許最大項(xiàng)數(shù),省略則無(wú)大小限制
q.put方法用以插入數(shù)據(jù)到隊(duì)列中,put方法還有兩個(gè)可選參數(shù):blocked和timeout。如果blocked為T(mén)rue(默認(rèn)值),并且timeout為正值,該方法會(huì)阻塞timeout指定的時(shí)間,直到該隊(duì)列有剩余的空間。如果超時(shí),會(huì)拋出Queue.Full異常。如果blocked為False,但該Queue已滿(mǎn),會(huì)立即拋出Queue.Full異常
q.get方法可以從隊(duì)列讀取并且刪除一個(gè)元素。同樣,get方法有兩個(gè)可選參數(shù):blocked和timeout。如果blocked為T(mén)rue(默認(rèn)值),并且timeout為正值,那么在等待時(shí)間內(nèi)沒(méi)有取到任何元素,會(huì)拋出Queue.Empty異常。如果blocked為False,有兩種情況存在,如果Queue有一個(gè)值可用,則立即返回該值,否則,如果隊(duì)列為空,則立即拋出Queue.Empty異常

操作

pipe管道
Pipe方法返回(conn1, conn2)代表一個(gè)管道的兩個(gè)端。Pipe方法有duplex參數(shù):duplex 為 True(默認(rèn)值),那么這個(gè)管道是全雙工模式,也就是說(shuō)conn1和conn2均可收發(fā)。duplex為 False,conn1只負(fù)責(zé)接受消息,conn2只負(fù)責(zé)發(fā)送消息
send和recv方法分別是發(fā)送和接收消息的方法。在全雙工模式下,可以調(diào)用conn1.send發(fā)送消息,conn1.recv接收消息。如果沒(méi)有消息可接收,recv方法會(huì)一直阻塞。如果管道已經(jīng)被關(guān)閉,那么recv方法會(huì)拋出EOFError