Resourcehtml
1、進程(process)python
狹義定義:進程就是一段程序的執行過程。編程
廣義定義:進程是一個具備必定獨立功能的程序關於某個數據集合的一次運行活動。它是操做系統動態執行的基本單元,在傳統的操做系統中,進程既是基本的分配單元,也是基本的執行單元。網絡
簡單的來說進程的概念主要有兩點:多線程
第一,進程是一個實體。每個進程都有它本身的地址空間,通常狀況下,包括:併發
文本區域(text region),本區域存儲處理器執行的代碼;app
數據區域(data region),存儲變量和進程執行期間使用的動態分配的內存;socket
堆棧(stack region),存儲着活動過程調用的指令和本地變量。高併發
第二,進程是一個「執行中的程序」。程序是一個沒有生命的實體,只有處理器賦予程序生命時,它才能成爲一個活動的實體,咱們稱其爲進程。ui
進程狀態:運行、阻塞、掛起阻塞、就緒、掛起就緒
就緒狀態其實就是獲取了出cpu外的全部資源,只要處理器分配資源就能夠立刻執行。就緒狀態有排隊序列什麼的,排隊原則再也不贅述。運行態就是得到了處理器分配的資源,程序開始執行。阻塞態,當程序條件不夠時候,須要等待條件知足時候才能執行,如等待i/o操做時候,此刻的狀態就叫阻塞態。
狀態之間的轉換:
(1)準備就緒的進程,被CPU調度執行,變成運行態;
(2)運行中的進程,進行I/O請求或者不能獲得所請求的資源,變成阻塞態;
(3)運行中的進程,進程執行完畢(或時間片已到),變成就緒態;
(4)將阻塞態的進程掛起,變成掛起阻塞態,當致使進程阻塞的I/O操做在用戶重啓進程前完成(稱之爲喚醒),掛起阻塞態變成掛起就緒態,當用戶在I/O操做結束以前重啓進程,掛起阻塞態變成阻塞態;
(5)將就緒(或運行)中的進程掛起,變成掛起就緒態,當該進程恢復以後,掛起就緒態變成就緒態;
2、程序
提及進程,就不得不說下程序。先看定義:程序是指令和數據的有序集合,其自己沒有任何運行的含義,是一個靜態的概念。而進程則是在處理機上的一次執行過程,它是一個動態的概念。這個不難理解,其實進程是包含程序的,進程的執行離不開程序,進程中的文本區域就是代碼區,也就是程序。
3、線程
一般在一個進程中能夠包含若干個線程,固然一個進程中至少有一個線程,否則沒有存在的意義。線程能夠利用進程所擁有的資源,在引入線程的操做系統中,一般都是把進程做爲分配資源的基本單位,而把線程做爲獨立運行和獨立調度的基本單位,因爲線程比進程更小,基本上不擁有系統資源,故對它的調度所付出的開銷就會小得多,能更高效的提升系統多個程序間併發執行的程度。
線程是進程中執行運算的最小單位,是進程中的一個實體,是被系統獨立調度和分派的基本單位,線程本身不擁有系統資源,只擁有一點在運行中必不可少的資源,但它可與同屬一個進程的其它線程共享進程所擁有的所有資源。一個線程能夠建立和撤消另外一個線程,同一進程中的多個線程之間能夠併發執行。
(1)易於調度。
(2)提升併發性。經過線程可方便有效地實現併發性。進程可建立多個線程來執行同一程序的不一樣部分。
(3)開銷少。建立線程比建立進程要快,所需開銷不多
4、多線程
在一個程序中,這些獨立運行的程序片斷叫做「線程」(Thread),利用它編程的概念就叫做「多線程處理」。多線程是爲了同步完成多項任務,不是爲了提升運行效率,而是爲了提升資源使用效率來提升系統的效率。線程是在同一時間須要完成多項任務的時候實現的。
最簡單的比喻多線程就像火車的每一節車箱,而進程則是火車。車箱離開火車是沒法跑動的,同理火車也不可能只有一節車箱。多線程的出現就是爲了提升效率。
(1)一個線程只能屬於一個進程,而一個進程能夠有多個線程,但至少有一個線程。
(2)資源分配給進程,同一進程的全部線程共享該進程的全部資源。
(3)處理機分給線程,即真正在處理機上運行的是線程
(4)線程在執行過程當中,須要協做同步。不一樣進程的線程間要利用消息通訊的辦法實現同步。線程是指進程內的一個執行單元,也是進程內的可調度實體.
(1)調度:線程做爲調度和分配的基本單位,進程做爲擁有資源的基本單位
(2)併發性:不只進程之間能夠併發執行,同一個進程的多個線程之間也可併發執行
(3)擁有資源:進程是擁有資源的一個獨立單位,線程不擁有系統資源,但能夠訪問隸屬於進程的資源.
(4)系統開銷:在建立或撤消進程時,因爲系統都要爲之分配和回收資源,致使系統的開銷明顯大於建立或撤消線程時的開銷。
(1)管道(pipe)及有名管道(named pipe):管道可用於具備親緣關係的父子進程間的通訊,有名管道除了具備管道所具備的功能外,它還容許無親緣關係進程間的通訊。
(2)信號(signal):信號是在軟件層次上對中斷機制的一種模擬,它是比較複雜的通訊方式,用於通知進程有某事件發生,一個進程收到一個信號與處理器收到一箇中斷請求效果上能夠說是一致的。
(3)消息隊列(message queue):消息隊列是消息的連接表,它克服了上兩種通訊方式中信號量有限的缺點,具備寫權限得進程能夠按照必定得規則向消息隊列中添加新信息;對消息隊列有讀權限得進程則能夠從消息隊列中讀取信息。
(4)共享內存(shared memory):能夠說這是最有用的進程間通訊方式。它使得多個進程能夠訪問同一塊內存空間,不一樣進程能夠及時看到對方進程中對共享內存中數據得更新。這種方式須要依靠某種同步操做,如互斥鎖和信號量等。
(5)信號量(semaphore):主要做爲進程之間及同一種進程的不一樣線程之間得同步和互斥手段。
(6)套接字(socket):這是一種更爲通常得進程間通訊機制,它可用於網絡中不一樣機器之間的進程間通訊,應用很是普遍。
當有多個線程的時候,常常須要去同步這些線程以訪問同一個數據或資源。例如,假設有一個程序,其中一個線程用於把文件讀到內存,而另外一個線程用於統計文件中的字符數。固然,在把整個文件調入內存以前,統計它的計數是沒有意義的。可是,因爲每一個操做都有本身的線程,操做系統會把兩個線程看成是互不相干的任務分別執行,這樣就可能在沒有把整個文件裝入內存時統計字數。爲解決此問題,你必須使兩個線程同步工做。
所謂同步,是指散步在不一樣進程之間的若干程序片段,它們的運行必須嚴格按照規定的某種前後次序來運行,這種前後次序依賴於要完成的特定的任務。若是用對資源的訪問來定義的話,同步是指在互斥的基礎上(大多數狀況),經過其它機制實現訪問者對資源的有序訪問。在大多數狀況下,同步已經實現了互斥,特別是全部寫入資源的狀況一定是互斥的。少數狀況是指能夠容許多個訪問者同時訪問資源。
所謂互斥,是指散佈在不一樣進程之間的若干程序片段,當某個進程運行其中一個程序片斷時,其它進程就不能運行它們之中的任一程序片斷,只能等到該進程運行完這個程序片斷後才能夠運行。若是用對資源的訪問來定義的話,互斥某一資源同時只容許一個訪問者對其進行訪問,具備惟一性和排它性。但互斥沒法限制訪問者對資源的訪問順序,即訪問是無序的。
import threading import time def show(arg): time.sleep(1) print 'thread'+str(arg) for i in range(10): t = threading.Thread(target=show, args=(i,)) t.start() print 'main thread stop'
import time import threading def run(n): print('[%s]------running----\n' % n) time.sleep(2) print('--done--') def main(): for i in range(5): t = threading.Thread(target=run,args=[i,]) #time.sleep(1) t.start() t.join(1) print('starting thread', t.getName()) m = threading.Thread(target=main,args=[]) m.setDaemon(True) #將主線程設置爲Daemon線程,它退出時,其它子線程會同時退出,無論是否執行完任務 m.start() #m.join(timeout=2) print("---main thread done----")
一個進程下能夠啓動多個線程,多個線程共享父進程的內存空間,也就意味着每一個線程能夠訪問同一份數據,此時,若是2個線程同時要修改同一份數據,會出現什麼情況?
import time import threading def addNum(): global num #在每一個線程中都獲取這個全局變量 print('--get num:',num ) time.sleep(1) num -=1 #對此公共變量進行-1操做 num = 100 #設定一個共享變量 thread_list = [] for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: #等待全部線程執行完畢 t.join() print('final num:', num )
正常來說,這個num結果應該是0, 但在python 2.7上多運行幾回,會發現,最後打印出來的num結果不老是0,爲何每次運行的結果不同呢? 哈,很簡單,假設你有A,B兩個線程,此時都 要對num 進行減1操做, 因爲2個線程是併發同時運行的,因此2個線程頗有可能同時拿走了num=100這個初始變量交給cpu去運算,當A線程去處完的結果是99,但此時B線程運算完的結果也是99,兩個線程同時CPU運算的結果再賦值給num變量後,結果就都是99。那怎麼辦呢? 很簡單,每一個線程在要修改公共數據時,爲了不本身在還沒改完的時候別人也來修改此數據,能夠給這個數據加一把鎖, 這樣其它線程想修改此數據時就必須等待你修改完畢並把鎖釋放掉後才能再訪問此數據。
注:不要在3.x上運行,不知爲何,3.x上的結果老是正確的,多是自動加了鎖
加鎖版本:
import time import threading def addNum(): global num #在每一個線程中都獲取這個全局變量 print('--get num:',num ) time.sleep(1) lock.acquire() #修改數據前加鎖 num -=1 #對此公共變量進行-1操做 lock.release() #修改後釋放 num = 100 #設定一個共享變量 thread_list = [] lock = threading.Lock() #生成全局鎖 for i in range(100): t = threading.Thread(target=addNum) t.start() thread_list.append(t) for t in thread_list: #等待全部線程執行完畢 t.join() print('final num:', num )
說白了就是在一個大鎖中還要再包含子鎖
import threading,time def run1(): print("grab the first part data") lock.acquire() global num num +=1 lock.release() return num def run2(): print("grab the second part data") lock.acquire() global num2 num2+=1 lock.release() return num2 def run3(): lock.acquire() res = run1() print('--------between run1 and run2-----') res2 = run2() lock.release() print(res,res2) if __name__ == '__main__': num,num2 = 0,0 lock = threading.RLock() for i in range(10): t = threading.Thread(target=run3) t.start() while threading.active_count() != 1: print(threading.active_count()) else: print('----all threads done---') print(num,num2)
互斥鎖 同時只容許一個線程更改數據,而Semaphore是同時容許必定數量的線程更改數據 ,好比廁全部3個坑,那最多隻容許3我的上廁所,後面的人只能等裏面有人出來了才能再進去。
import threading,time def run(n): semaphore.acquire() time.sleep(1) print("run the thread: %s\n" %n) semaphore.release() if __name__ == '__main__': num= 0 semaphore = threading.BoundedSemaphore(5) #最多容許5個線程同時運行 for i in range(20): t = threading.Thread(target=run,args=(i,)) t.start() while threading.active_count() != 1: pass #print threading.active_count() else: print('----all threads done---') print(num)
Python提供了Event對象用於線程間通訊,它是由線程設置的信號標誌,若是信號標誌位真,則其餘線程等待直到信號接觸。
Event對象實現了簡單的線程通訊機制,它提供了設置信號,清楚信號,等待等用於實現線程間的通訊。
Event對象wait的方法只有在內部信號爲真的時候纔會很快的執行並完成返回。當Event對象的內部信號標誌位假時,則wait方法一直等待到其爲真時才返回。
使用Event的set()方法能夠設置Event對象內部的信號標誌爲真。Event對象提供了isSet()方法來判斷其內部信號標誌的狀態。當使用event對象的set()方法後,isSet()方法返回真
使用Event對象的clear()方法能夠清除Event對象內部的信號標誌,即將其設爲假,當使用Event的clear方法後,isSet()方法返回假