細分的話,操做系統應該分紅兩部分功能:html
#一:隱藏了醜陋的硬件調用接口,爲應用程序員提供調用硬件資源的更好,更簡單,更清晰的模型(系統調用接口)。應用程序員有了這些接口後,就不用再考慮操做硬件的細節,專心開發本身的應用程序便可。
例如:操做系統提供了文件這個抽象概念,對文件的操做就是對磁盤的操做,有了文件咱們無需再去考慮關於磁盤的讀寫控制(好比控制磁盤轉動,移動磁頭讀寫數據等細節),
#二:將應用程序對硬件資源的競態請求變得有序化
例如:不少應用軟件實際上是共享一套計算機硬件,比方說有可能有三個應用程序同時須要申請打印機來輸出內容,那麼a程序競爭到了打印機資源就打印,而後多是b競爭到打印機資源,也多是c,這就致使了無序,打印機可能打印一段a的內容而後又去打印c...,操做系統的一個功能就是將這種無序變得有序。
進程(Process)是計算機中的程序關於某數據集合上的一次運行活動,是系統進行資源分配和調度的基本單位,是操做系統結構的基礎.在早期面向進程設計的計算機結構中,進程是程序的基本執行實體:在當代面向線程設計的計算機結構中,進程是線程的容器.程序是指令,數據及其組織形式的描述,進程是程序的實體.python
注意 : 同一個程序執行兩次,就會在操做系統中出現兩個進程,因此咱們能夠同時運行一個軟件,分別作不一樣的事情也不會混亂.linux
並行 : 並行指二者同時進行 , 好比跑賽,兩我的都在不停的向前跑 : (資源夠用)
程序員
併發 : 併發指在資源有限的狀況下,二者交替輪流使用資源.好比一張大餅(單核CPU),兩我的都很是餓,可是吃的時候只能有一我的吃,那麼就是A吃一口,讓給B,B再吃一口然給A,交替使用,目的是提升效率.面試
區別 : 編程
並行 : 並行是從微觀上,也就是說在一個精確的時間片刻,有不一樣的程序在執行,這就要求必須有多個處理器安全
併發 : 併發是從宏觀上,在一個時間段裏能夠看出是同時執行的,好比一個服務器同時處理多個程序.(好比,一遍看視頻,一遍聊天.)服務器
在Windows操做系統中因爲沒有fork(linux操做系統中建立進程的機制),在建立子進程的時候會自動 import 啓動它的這個文件,而在 import 的時候又執行了整個文件。所以若是將process()直接寫在文件中就會無限遞歸建立子進程報錯。因此必須把建立子進程的部分使用if __name__ ==‘__main__’ 判斷保護起來,import 的時候 ,就不會遞歸運行了。
進程的建立 網絡
1 p.daemon:默認值爲False,若是設爲True,表明p爲後臺運行的守護進程,當p的父進程終止時,p也隨之終止,而且設定爲True後,p不能建立本身的新進程,必須在p.start()以前設置 2 p.name:進程的名稱 3 p.pid:進程的pid / ident---進程的id 4 p.exitcode:進程在運行時爲None、若是爲–N,表示被信號N結束(瞭解便可) 5 p.authkey:進程的身份驗證鍵,默認是由os.urandom()隨機生成的32字符的字符串。這個鍵的用途是爲涉及網絡鏈接的底層進程間通訊提供安全性,這類鏈接只有在具備相同的身份驗證鍵時才能成功(瞭解便可) 6 p.join([timeout]):主線程等待p終止(強調:是主線程處於等的狀態,而p是處於運行的狀態)。timeout是可選的超時時間,須要強調的是,p.join只能join住start開啓的進程,而不能join住run開啓的進程
基本操做 多線程
import multiprocessing def func(arg): print(arg) if __name__ == '__main__': for i in range(10): t = multiprocessing.Process(target=func,args=(i,)) t.start()
● 進程間的數據不共享
import multiprocessing li = [] def func(arg): li.append(arg) print(li) def run(): for i in range(10): t = multiprocessing.Process(target=func,args=(i,)) t.start() if __name__ == '__main__': run() 結果: 每個數都是一個進程 [0] [1] [2] [3] [4] [5] [6] [7] [8] [9]
● 進程通用功能 -- name
import multiprocessing import time def func(arg): time.sleep(2) print(arg) def run(): print("666") p1 = multiprocessing.Process(target=func,args=(1,)) # p1.name = "pig" #進程名稱 # print(p1.name) p1.start() print("888") p2 = multiprocessing.Process(target=func,args=(2,)) # p2.name = "dog" p2.start() print("999") if __name__ == '__main__': run()
●開發者能夠控制主進程等待子進程(最多等待時間) join()
import multiprocessing import time def func(arg): time.sleep(5) print(arg) def run(): print("666") p1 = multiprocessing.Process(target=func,args=(1,)) p1.start() # p1.join(1) print("888") p1.join(1) p2 = multiprocessing.Process(target=func,args=(2,)) p2.start() # p1.join(1) print("999") p2.join(1) if __name__ == '__main__': run() 結果: 666 888 #2秒後 999 #5秒後 1 2
注意:join() :
無參數,讓主線程在這裏等着,等到子線程t1執行完畢,才能夠繼續往下走。
有參數,讓主線程在這裏最多等待n秒,不管是否執行完畢,會繼續往下走。
● 守護進程 Daemon
會隨着主進程的結束而結束
主進程建立守護進程
其一:守護進程會在主進程代碼執行結束後就終止
其二:守護進程內沒法再開啓子進程,不然拋出異常:AssertionError: daemonic processes are not allowed to have children
注意:進程之間是互相獨立的,主進程代碼運行結束,守護進程隨即終止
import os import time from multiprocessing import Process class Myprocess(Process): def __init__(self,person): super().__init__() self.person = person def run(self): print(os.getpid(),self.name) print('%s正在和女主播聊天' %self.person) p=Myprocess('託塔') p.daemon=True #必定要在p.start()前設置,設置p爲守護進程,禁止p建立子進程,而且父進程代碼執行結束,p即終止運行 p.start() time.sleep(10) # 在sleep時查看進程id對應的進程ps -ef|grep id print('主')
import multiprocessing import time def func(arg): time.sleep(5) print(arg) def run(): print("666") p1 = multiprocessing.Process(target=func,args=(1,)) #daemon 也能夠放這裏 p1.daemon = True p1.start() print("888") p2 = multiprocessing.Process(target=func,args=(2,)) p2.daemon = True p2.start() print("999") if __name__ == '__main__': run()
● 獲取id pid / ident
import multiprocessing import time def func(arg): time.sleep(5) print(arg) def run(): print("666") p1 = multiprocessing.Process(target=func,args=(1,)) p1.start() print(p1.pid) p2 = multiprocessing.Process(target=func,args=(2,)) p2.start() print(p2.pid) if __name__ == '__main__': run() 結果: 666 4808 15240 1 2
類繼承的方式
class MyProcess(multiprocessing.Process): def run(self): print('當前進程',multiprocessing.current_process()) def run(): p1 = MyProcess() p1.start() p2 = MyProcess() p2.start() if __name__ == '__main__': run() 結果: 當前進程 <MyProcess(MyProcess-1, started)> 當前進程 <MyProcess(MyProcess-2, started)>
進程間的數據共享
Queue
建立共享的進程隊列,Queue是多進程安全的隊列,可使用Queue實現多進程之間的數據傳遞.
Queue([maxsize])
建立共享的進程隊列。
參數 :maxsize是隊列中容許的最大項數。若是省略此參數,則無大小限制。
底層隊列使用管道和鎖定實現。
Queue([maxsize])
建立共享的進程隊列。maxsize是隊列中容許的最大項數。若是省略此參數,則無大小限制。底層隊列使用管道和鎖定實現。另外,還須要運行支持線程以便隊列中的數據傳輸到底層管道中。
Queue的實例q具備如下方法:
q.get( [ block [ ,timeout ] ] )
返回q中的一個項目。若是q爲空,此方法將阻塞,直到隊列中有項目可用爲止。block用於控制阻塞行爲,默認爲True. 若是設置爲False,將引起Queue.Empty異常(定義在Queue模塊中)。timeout是可選超時時間,用在阻塞模式中。若是在制定的時間間隔內沒有項目變爲可用,將引起Queue.Empty異常。
q.get_nowait( )
同q.get(False)方法。
q.put(item [, block [,timeout ] ] )
將item放入隊列。若是隊列已滿,此方法將阻塞至有空間可用爲止。block控制阻塞行爲,默認爲True。若是設置爲False,將引起Queue.Empty異常(定義在Queue庫模塊中)。timeout指定在阻塞模式中等待可用空間的時間長短。超時後將引起Queue.Full異常。
q.qsize()
返回隊列中目前項目的正確數量。此函數的結果並不可靠,由於在返回結果和在稍後程序中使用結果之間,隊列中可能添加或刪除了項目。在某些系統上,此方法可能引起NotImplementedError異常。
q.empty()
若是調用此方法時 q爲空,返回True。若是其餘進程或線程正在往隊列中添加項目,結果是不可靠的。也就是說,在返回和使用結果之間,隊列中可能已經加入新的項目。
q.full()
若是q已滿,返回爲True. 因爲線程的存在,結果也多是不可靠的(參考q.empty()方法)。。
方法介紹
示例 :
import multiprocessing q = multiprocessing.Queue() def task(arg, q): q.put(arg) def run(): for i in range(10): p = multiprocessing.Process(target=task, args=(i, q,)) p.start() while True: v = q.get() print(v) run()
import multiprocessing def task(arg, q): q.put(arg) if __name__ == '__main__': q = multiprocessing.Queue() for i in range(10): p = multiprocessing.Process(target=task, args=(i, q,)) p.start() while True: v = q.get() print(v) 結果: 0 1 2 3 4 5 6 7 8 9
Manager ***
進程間數據是獨立的,能夠藉助於隊列或管道實現通訊,兩者都是基於消息傳遞的 雖然進程間數據獨立,但能夠經過Manager實現數據共享,事實上Manager的功能遠不止於此 A manager object returned by Manager() controls a server process which holds Python objects and allows other processes to manipulate them using proxies. A manager returned by Manager() will support types list, dict, Namespace, Lock, RLock, Semaphore, BoundedSemaphore, Condition, Event, Barrier, Queue, Value and Array.
示例 :
import multiprocessing import time def task(arg, dic): time.sleep(2) dic[arg] = 100 if __name__ == '__main__': m = multiprocessing.Manager() dic = m.dict() process_list = [] for i in range(10): p = multiprocessing.Process(target=task, args=(i, dic,)) p.start() process_list.append(p) while True: count = 0 for p in process_list: if not p.is_alive(): count += 1 if count == len(process_list): break print(dic) 結果: {0: 100, 1: 100, 2: 100, 3: 100, 4: 100, 5: 100, 6: 100, 7: 100, 8: 100, 9: 100}
import multiprocessing m = multiprocessing.Manager() dic = m.dict() def task(arg): dic[arg] = 100 def run(): for i in range(10): p = multiprocessing.Process(target=task, args=(i,)) p.start() input('>>>') print(dic.values()) if __name__ == '__main__': run()
進程鎖
進程鎖與線程鎖的方法是同樣的,也是有Lock , RLock , Semaphore , Event , Condition.
RLock
import time import threading import multiprocessing lock = multiprocessing.RLock() def task(arg): print("我真帥") lock.acquire() time.sleep(2) print(arg) lock.release() if __name__ == '__main__': p1 = multiprocessing.Process(target=task,args=(1,)) p1.start() p2 = multiprocessing.Process(target=task, args=(2,)) p2.start() 結果: 我真帥 我真帥 1 2
進程池
爲何要有進程池?進程池的概念。
在程序實際處理問題過程當中,忙時會有成千上萬的任務須要被執行,閒時可能只有零星任務。那麼在成千上萬個任務須要被執行的時候,咱們就須要去建立成千上萬個進程麼?首先,建立進程須要消耗時間,銷燬進程也須要消耗時間。第二即使開啓了成千上萬的進程,操做系統也不能讓他們同時執行,這樣反而會影響程序的效率。所以咱們不能無限制的根據任務開啓或者結束進程。那麼咱們要怎麼作呢?
定義一個池子,在裏面放上固定數量的進程,有需求來了,就拿一個池中的進程來處理任務,等處處理完畢,進程並不關閉,而是將進程再放回進程池中繼續等待任務。若是有不少任務須要執行,池中的進程數量不夠,任務就要等待以前的進程執行任務完畢歸來,拿到空閒進程才能繼續執行。也就是說,池中進程的數量是固定的,那麼同一時間最多有固定數量的進程在運行。這樣不會增長操做系統的調度難度,還節省了開閉進程的時間,也必定程度上可以實現併發效果。
import time from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor def task(arg): time.sleep(2) print(arg) if __name__ == '__main__': pool = ProcessPoolExecutor(5) for i in range(10): pool.submit(task,i) 結果: #2秒後 0 2 1 3 4 #2秒後 5 7 6 8 9
因此,咱們能夠利用進程池,寫一個小爬蟲的實例:
import re from urllib.request import urlopen from multiprocessing import Pool def get_page(url,pattern): response=urlopen(url).read().decode('utf-8') return pattern,response def parse_page(info): pattern,page_content=info res=re.findall(pattern,page_content) for item in res: dic={ 'index':item[0].strip(), 'title':item[1].strip(), 'actor':item[2].strip(), 'time':item[3].strip(), } print(dic) if __name__ == '__main__': regex = r'<dd>.*?<.*?class="board-index.*?>(\d+)</i>.*?title="(.*?)".*?class="movie-item-info".*?<p class="star">(.*?)</p>.*?<p class="releasetime">(.*?)</p>' pattern1=re.compile(regex,re.S) url_dic={ 'http://maoyan.com/board/7':pattern1, } p=Pool() res_l=[] for url,pattern in url_dic.items(): res=p.apply_async(get_page,args=(url,pattern),callback=parse_page) res_l.append(res) for i in res_l: i.get()
程序並不能單獨運行,只有將程序裝載到內存中,系統爲它分配資源才能運行,而這種執行的程序就稱之爲進程。程序和進程的區別就在於:程序是指令的集合,它是進程運行的靜態描述文本;進程是程序的一次執行活動,屬於動態概念。在多道編程中,咱們容許多個程序同時加載到內存中,在操做系統的調度下,能夠實現併發地執行。這是這樣的設計,大大提升了CPU的利用率。進程的出現讓每一個用戶感受到本身獨享CPU,所以,進程就是爲了在CPU上實現多道編程而提出的。
進程有不少優勢,它提供了多道編程,讓咱們感受咱們每一個人都擁有本身的CPU和其餘資源,能夠提升計算機的利用率。可是進程仍是有不少缺陷的,主要體如今兩點上:
進程只能在一個時間幹一件事,若是想同時幹兩件事或多件事,進程就無能爲力了。
進程在執行的過程當中若是阻塞,例如等待輸入,整個進程就會掛起,即便進程中有些工做不依賴於輸入的數據,也將沒法執行
好比:若是把咱們在寫代碼的時候想要寫代碼的時候能夠聽歌和聊天.可是若是隻是提供進程這個機制的話,那麼咱們就只能寫完代碼,在聽歌,而後再聊天,不能再同時進行.
因此咱們要解決這個問題,咱們徹底可讓寫代碼,聽歌,聊天三個獨立的過程,並行起來,這樣很明顯能夠提升效率。而實際的操做系統中,也一樣引入了這種相似的機制——線程。
線程與進程的區別 :
● 地址空間和其餘資源(好比打開文件) : 進程之間相互獨立,同一進程的各線程之間共享資源.某進程內的線程和其餘進程內的線程是不可見的.
● 通訊 : 進程間通訊IPC,線程間能夠直接讀寫進程數據段(如全局變量)來進行通訊(--須要進程同步和互斥手段的輔助,以保證數據的一致性).
● 調度和切換 : 線程上下文切換比進程上下文切換要快的多.
● 在多線程操做系統中,進程不是一個可執行的實體.
進程和線程的區別(面試):
1.進程是CPU分配的最小單元.
線程是CPU計算的最早單元.
2.一個進程能夠有多個線程.
3.對於Python來講,它的進程和線程和其餘語言有差別,是有GIL鎖的
GIL鎖保證一個進程中同一時刻只有一個線程被CPU調度.
在多線程的操做系統中,一般是在一個進程中包括多個線程,每個線程都是做爲利用CPU的基本單元,是花費最小開銷的實體.
線程的屬性 :
● 輕型實體
線程上的實體基本上不擁有系統資源,只是有一點必不可少的,能保證獨立運行的資源.
線程的實體包括程序,數據和TCB.線程是動態概念,他的動態特性由線程控制塊TCB描述.
TCB包括如下信息: (1)線程狀態。 (2)當線程不運行時,被保存的現場資源。 (3)一組執行堆棧。 (4)存放每一個線程的局部變量主存區。 (5)訪問同一個進程中的主存和其它資源。 用於指示被執行指令序列的程序計數器、保留局部變量、少數狀態參數和返回地址等的一組寄存器和堆棧。
● 獨立調度和分派的基本單位.
在多線程OS中,線程是能獨立運行的最小單位,於是也是獨立調度和分派的基本單位,因爲現場很"輕",故線程的切換很是迅速且開銷小(在同一進程中的線程.)
● 共享進程資源
線程在同一進程中的各個線程,均可以共享該進程所擁有的資源,這首先表如今 : 全部線程都具備相同的進程id,意味着,線程能夠訪問該進程的每個內存資源:此外,還能夠訪問進程所擁有的已經打開文件,定時器,信號量機構等.因爲同一個進程內的線程共享內存和文件,全部線程直接互相通訊沒必要調用內核.
● 可併發執行
在同一個進程的多個線程之間,能夠併發執行,甚至容許在一個進程中全部線程都能併發執行:一樣,不一樣進程中的線程也能併發執行,充分利用和發揮了處理機制與外圍設備並行工做的能力.
開啓一個字處理軟件進程,該進程確定須要辦不止一件事情,好比監聽鍵盤輸入,處理文字,定時自動將文字保存到硬盤,這三個任務操做的都是同一塊數據,於是不能用多進程。只能在一個進程裏併發地開啓三個線程,若是是單線程,那就只能是,鍵盤輸入時,不能處理文字和自動保存,自動保存時又不能輸入和處理文字。
Python中本身是沒有線程和進程的,Python是調用操做系統中的線程和進程.
Python代碼的執行由Python虛擬機(也叫解釋器主循環)來控制。Python在設計之初就考慮到要在主循環中,同時只有一個線程在執行。雖然 Python 解釋器中能夠「運行」多個線程,但在任意時刻只有一個線程在解釋器中運行。
對Python虛擬機的訪問由全局解釋器鎖(GIL)來控制,因此,GIL鎖的做用就是保證一個進程中在同一時刻只要一個線程能夠運行(被CPU調度).
注意 : 默認GIL鎖在執行100個CPU指令後換另一個線程執行.
因此 , 因爲在Python中存在一個GIL鎖:
---致使 : 多線程沒法利用多核的優點
---解決 : 開多進程處理(浪費資源)
#基本使用 import threading def func(arg): print(arg) t = threading.Thread(target=func,args=(112,)) #子線程 t.start() print(123) #主線程 結果: 112 123
● 主線程默認等子線程執行完畢
import threading import time def func(arg): time.sleep(arg) print(arg) t1 = threading.Thread(target=func,args=(1,)) t1.start() t2 = threading.Thread(target=func,args=(3,)) t2.start() print("我真帥") 結果: 我真帥 #1秒後 1 #3秒後 3
● 主線程再也不等,主線程終止則因此線程終止 setDaemon()
import threading import time def func(arg): time.sleep(2) print(arg) t1 = threading.Thread(target=func,args=(3,)) t1.setDaemon(True) #使主線程不等子線程 t1.start() t2 = threading.Thread(target=func,args=(5,)) t2.setDaemon(True) t2.start() print("我真帥")
●開發者能夠控制主線程等待子線程(最多等待時間) join()
import threading import time def func(arg): time.sleep(arg) print(arg) print("我是有多帥") t1 = threading.Thread(target=func,args=(3,)) t1.start() t1.join() print("很是帥") t2 = threading.Thread(target=func,args=(5,)) t2.start() t2.join() print("大實話") 結果: 我是有多帥 #3秒後 3 很是帥 #5秒後 5 大實話
import threading import time def func(arg): time.sleep(arg) print(arg) print("我是有多帥") t1 = threading.Thread(target=func,args=(5,)) t1.start() t1.join(2) print("很是帥") t2 = threading.Thread(target=func,args=(9,)) t2.start() t2.join(2) print("大實話") 結果: 我是有多帥 #2秒後 很是帥 #2秒後 大實話 #5秒後(和大實話差1秒) 5 #9秒後 9
注意:join() :
無參數,讓主線程在這裏等着,等到子線程t1執行完畢,才能夠繼續往下走。
有參數,讓主線程在這裏最多等待n秒,不管是否執行完畢,會繼續往下走。
● 線程名稱 srtName()
import threading def func(arg): # 獲取當前執行該函數的線程的對象 t = threading.current_thread() # 根據當前線程對象獲取當前線程名稱 name = t.getName() print(name,arg) t1 = threading.Thread(target=func,args=(11,)) t1.setName("張三") #使線程有名稱 t1.start() t2 = threading.Thread(target=func,args=(22,)) t2.setName('李四') t2.start() print(123)
● 線程本質
import threading def func(arg): print(arg) t1 = threading.Thread(target=func,args=(66,)) t1.start() # start 是開始運行線程嗎?不是 # start 告訴cpu,我已經準備就緒,你能夠調度我了。 print("我好看") 結果: 66 我好看
● 面向對象的多線程
import threading 線程方式:1 (常見) def func(arg): print(arg) t1 = threading.Thread(target=func,args=(11,)) t1.start() # 多線程方式:2 class MyThread(threading.Thread): def run(self): print(11111,self._args,self._kwargs) t1 = MyThread(args=(11,)) t1.start() t2 = MyThread(args=(22,)) t2.start() print('end')
● IO操做
import threading import requests import uuid url_list = [ 'https://www3.autoimg.cn/newsdfs/g28/M05/F9/98/120x90_0_autohomecar__ChsEnluQmUmARAhAAAFES6mpmTM281.jpg', 'https://www2.autoimg.cn/newsdfs/g28/M09/FC/06/120x90_0_autohomecar__ChcCR1uQlD6AT4P3AAGRMJX7834274.jpg', 'https://www2.autoimg.cn/newsdfs/g3/M00/C6/A9/120x90_0_autohomecar__ChsEkVuPsdqAQz3zAAEYvWuAspI061.jpg', ] def task(url): ret = requests.get(url) file_name = str(uuid.uuid4()) + '.jpg' with open(file_name, mode='wb') as f: f.write(ret.content) for url in url_list: t = threading.Thread(target=task,args=(url,)) t.start()
總結 :
1.Python中存在一個GIL鎖。
---形成:多線程沒法利用多核優點
---解決:開多進程處理(浪費資源)
2.IO密集型(文件的輸入/輸出/socket網絡通訊) : 多線程 (不佔用CPU)
計算密集型 : 多進程
3.爲何Python有GIL鎖?
答 : Python語言創始人在開發這門語言時,目的是快速把語言開發出來,若是加上GIL鎖(C語言加鎖),切換時按照100條字節指令來進行線程間的切換.
4.線程建立的越多越好嗎?
答:很差,線程之間進行切換時,要作上下文處理.
同步鎖 Lock(一次放一個)
線程安全:多線程操做時,內部會讓全部線程排隊處理.如:list / dict / Queue
線程不安全 + 人 => 排隊處理
建立100個線程 v = [] 加鎖 -把本身的添加到列表中.安全的 -讀取列表的最後一個.最後一個就不必定是本身的了,因此不安全 解鎖
import threading import time v = [] lock = threading.Lock() def func(arg): lock.acquire() 上鎖 v.append(arg) time.sleep(0.01) m = v[-1] print(arg,m) lock.release() 解鎖 for i in range(10): t = threading.Thread(target=func,args=(i,)) t.start() 之後鎖一個代碼塊
遞歸鎖 RLock(一次放一個) 與死鎖
所謂死鎖: 是指兩個或兩個以上的進程或線程在執行過程當中,因爭奪資源而形成的一種互相等待的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程,以下就是死鎖.
import threading import time v = [] lock = threading.Lock() def func(arg): lock.acquire() lock.acquire() v.append(arg) time.sleep(0.01) m = v[-1] print(arg,m) lock.release() lock.release() for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start()
解決方法,遞歸鎖,在Python中爲了支持在同一線程中屢次請求同一資源,python提供了可重入鎖RLock。
這個RLock內部維護着一個Lock和一個counter變量,counter記錄了acquire的次數,從而使得資源能夠被屢次require。直到一個線程全部的acquire都被release,其餘的線程才能得到資源。上面的例子若是使用RLock代替Lock,則不會發生死鎖:
import threading import time v = [] lock = threading.RLock() def func(arg): lock.acquire() lock.acquire() v.append(arg) time.sleep(0.01) m = v[-1] print(arg,m) lock.release() lock.release() for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start()
信號量 BoundedSemaphore (一次放n個)
同進程的同樣
BoundedSemaphore管理一個內置的計數器,
每當調用acquire()時內置計數器-1;
調用release() 時內置計數器+1;
計數器不能小於0;當計數器爲0時,acquire()將阻塞線程直到其餘線程調用release()。
實例:(同時只有3個線程能夠得到BoundedSemaphore,便可以限制最大鏈接數爲3):
import time import threading lock = threading.BoundedSemaphore(3) def func(arg): lock.acquire() print(arg) time.sleep(2) lock.release() for i in range(20): t =threading.Thread(target=func,args=(i,)) t.start() 結果: #2秒後 0 1 2 #2秒後 3 4 5 #2秒後 6 7 8 9 10 11 12 13 14 15 16 17 18 19
條件 Condition (一次放x個,根據條件)
使得線程等待,只有知足某條件時,才釋放n個線程
Python提供的Condition對象提供了對複雜線程同步問題的支持。Condition被稱爲條件變量,除了提供與Lock相似的acquire和release方法外,還提供了wait和notify方法。線程首先acquire一個條件變量,而後判斷一些條件。若是條件不知足則wait;若是條件知足,進行一些處理改變條件後,經過notify方法通知其餘線程,其餘處於wait狀態的線程接到通知後會從新判斷條件。不斷的重複這一過程,從而解決複雜的同步問題。
代碼說明:
import time import threading lock = threading.Condition() def func(arg): print('線程進來了') lock.acquire() lock.wait() # 加鎖 print(arg) time.sleep(1) lock.release() for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start() while True: inp = int(input('>>>')) lock.acquire() lock.notify(inp) lock.release() 結果: 線程進來了 線程進來了 線程進來了 線程進來了 線程進來了 線程進來了 線程進來了 線程進來了 線程進來了 線程進來了 >>>8 #輸入8. 由於誰也不知道CPU是先調用那個線程,因此沒有順序 >>>0 2 3 5 4 6 7 1 3 #輸入2 可是隻剩下2個 >>>8 9
import time import threading lock = threading.Condition() def xxxx(): print('來執行函數了') input(">>>") # ct = threading.current_thread() # 獲取當前線程 # ct.getName() return True def func(arg): print('線程進來了') lock.wait_for(xxxx) print(arg) time.sleep(1) for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start() #說明:先循環,調用func函數,打印"線程來了",等待調用函數xxxx,打印"來執行函數了",用戶輸入數字,返回arg = i ,因此不管輸入什麼都是一個線程執行.
事件 Event (一次放行)
同進程的同樣
線程的一個關鍵特性是每一個線程都是獨立運行且狀態不可預測。若是程序中的其 他線程須要經過判斷某個線程的狀態來肯定本身下一步的操做,這時線程同步問題就會變得很是棘手。爲了解決這些問題,咱們須要使用threading庫中的Event對象。 對象包含一個可由線程設置的信號標誌,它容許線程等待某些事件的發生。在 初始狀況下,Event對象中的信號標誌被設置爲假。若是有線程等待一個Event對象, 而這個Event對象的標誌爲假,那麼這個線程將會被一直阻塞直至該標誌爲真。一個線程若是將一個Event對象的信號標誌設置爲真,它將喚醒全部等待這個Event對象的線程。若是一個線程等待一個已經被設置爲真的Event對象,那麼它將忽略這個事件, 繼續執行
event.isSet():返回event的狀態值; event.wait():若是 event.isSet()==False將阻塞線程; event.set(): 設置event的狀態值爲True,全部阻塞池的線程激活進入就緒狀態, 等待操做系統調度; event.clear():恢復event的狀態值爲False。
import time import threading lock = threading.Event() def func(arg): print('線程來了') lock.wait() # 加鎖:紅燈 print(arg) for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start() input(">>>>") lock.set() # 綠燈 lock.clear() # 再次變紅燈 for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start() input(">>>>") lock.set() #放行
總結 : 爲何要加鎖?
---非線程安全
---控制一段代碼塊
from concurrent.futures import ThreadPoolExecutor import time def task(a1,a2): time.sleep(2) print(a1,a2) # 建立了一個線程池(最多5個線程) pool = ThreadPoolExecutor(5) for i in range(40): # 去線程池中申請一個線程,讓線程執行task函數。 pool.submit(task,i,8)
當咱們在考試的時候,會有收手機這個行爲,考場的老師會將咱們的手機放到一個收納盒中,等考試結束以後,才能夠拿本身的手機,可是這裏有一個弊端,會不會有人看到 iPhone X 就拿走了.可是當咱們爲每一個人都建立一個收納盒,放本身的手機在寫上名字,這樣是否是就不會拿錯了呢?
因此 , threading.local 的做用 : 內部自動爲每個線程維護一個空間(以字典形式),用於當前存取屬於本身的值,保證數據隔離.
{
線程ID: {...},
線程ID: {...},
線程ID: {...},
線程ID: {...}
}
示例 :
import time import threading v = threading.local() def func(arg): # 內部會爲當前線程建立一個空間用於存儲:phone=本身的值 v.phone = arg time.sleep(2) print(v.phone,arg) # 去當前線程本身空間取值 for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start() 結果: 0 0 1 1 4 4 2 2 5 5 3 3 6 6 7 7 9 9 8 8
""" 之後:Flask框架內部看到源碼 上下文管理 """ import time import threading INFO = {} class Local(object): def __getattr__(self, item): ident = threading.get_ident() return INFO[ident][item] def __setattr__(self, key, value): ident = threading.get_ident() if ident in INFO: INFO[ident][key] = value else: INFO[ident] = {key:value} obj = Local() def func(arg): obj.phone = arg # 調用對象的 __setattr__方法(「phone」,1) time.sleep(2) print(obj.phone,arg) for i in range(10): t =threading.Thread(target=func,args=(i,)) t.start()
生產者消費者模型
當咱們在節假日或者春運的時候買車票,咱們買票的和賣票的之間存在着必定的關係,由於賣票的不知道是要多少人才能知足人買票的需求,這就須要排隊.這樣就會形成必定的事件.可是,當咱們在買票和賣票的之間放一根管道:
這樣,咱們能夠把需求放入管道里,就能夠去作別的事情,當票打印以後再通知你,這樣就能夠兩不當誤.咱們稱管道叫作:隊列.
線程隊列
queue隊列: 使用 import queue
queue is especially useful in threaded programming when information must be exchanged safely between multiple threads.
(隊列在線程編程中特別有用,當信息必須在多個線程之間安全交換時。)
queue.Queue() #先進先出
import queue q=queue.Queue() q.put('first') q.put('second') q.put('third') print(q.get()) print(q.get()) print(q.get()) 結果(先進先出): first second third
示例 :
import time import queue import threading q = queue.Queue() # 線程安全 def producer(id): """ 生產者 :return: """ while True: time.sleep(2) q.put('包子') print('廚師%s 生產了一個包子' %id ) for i in range(1,4): t = threading.Thread(target=producer,args=(i,)) t.start() def consumer(id): """ 消費者 :return: """ while True: time.sleep(1) v1 = q.get() print('顧客 %s 吃了一個包子' % id) for i in range(1,3): t = threading.Thread(target=consumer,args=(i,)) t.start()