進程定義:python
進程就是一個程序在一個數據集上的一次動態執行過程.安全
進程通常由程序,數據集,進程控制塊三部分組成多線程
咱們編寫的程序用來描述進程要完成哪些功能以及如何完成併發
數據集則是程序在執行過程當中所須要使用的資源app
進程控制塊用來記錄進程的外部特徵,描述進程的執行變化過程,系統能夠利用它啦控制和管理進 程,他是系統感知進程存在的惟一標誌框架
線程定義:異步
線程也叫輕量級進程, 他是一個基本的CPU執行單元, 也是程序執行過程當中的最小單元,由線程ID, 程序,計數器,寄存器集合和堆棧共同組成.線程的引入減少了程序併發執行時的開銷,提升了操做系統的併發性能.ide
線程沒有本身的系統資源函數
1.一個程序至少有一個進程,一個進程至少有一個線程.(進程能夠理解成線程的容器)性能
2.進程在執行過程當中擁有獨立內存單元,而多個線程共享內存,從而極大地提升了程序的運行效率
3.線程在執行過程當中與進程仍是有區別的.每一個獨立的線程有一個程序運行的入口,孫旭執行序列和程序的出口.可是線程不能獨立執行,必須依存在應用程序中,有應用程序提供多個線程執行控制
4.進程是具備必定獨立功能的程序關於某個數據集合上的一次運行活動,進程是系統進行資源分配和調度的一個獨立單位.線程是進程的一個實體,是CPU調度和分派的基本單位,它是比進程更小的能獨立運行的基本單位.線程 本身基本上不擁有系統資源,只擁有一點在運行中必不可少的資源(如程序計數器,一組寄存器和棧)可是它可與同屬一個進程的其餘的線程共享進程所擁有的所有資源
一個線程能夠建立和撤銷另外一個線程;同一個進程中的多個線程之間能夠併發執行
GIL即python中的全局變量鎖,它維護着線程安全,但前提是限制了單個主線程中的子線程同時運行,不管你開啓多少個線程,你有多少個CPU,python在執行的時候會限制在同一時刻只容許一個線程運行
第一種
1 import threading 2 import time 3 4 5 6 def music(): 7 print("start to listen %s"%time.ctime()) 8 time.sleep(3) 9 print("end to listen %s"%time.ctime()) 10 11 def game(): 12 print("start to playgame %s"%time.ctime()) 13 time.sleep(5) 14 print("end to playgame %s"%time.ctime()) 15 16 17 if __name__ == '__main__': 18 t1 = threading.Thread(target=music) #實例化一個子線程 19 t2 = threading.Thread(target=game) 20 21 t1.start() #執行子線程 22 t2.start() 23 24 t2.join() #等待子線程結束後執行後面代碼 25 t1.join() 26 27 print("======ending=========") #主線程與子線程併發,若是存在join,則需等待子線程結束後方可執行 28 29 直接調用
第二種
1 import threading 2 import time 3 4 class MyThread(threading.Thread): 5 def __init__(self,num): 6 threading.Thread.__init__(self) 7 self.num = num 8 def run(self): #定義每一個線程要運行的函數 9 print("running on number:%s" % self.num) 10 11 time.sleep(3) 12 13 if __name__ == '__main__': 14 t1 = MyThread(1) 15 t2 = MyThread(2) 16 t1.start() 17 t2.start() 18 19 print("ending......") 20 21 繼承
join():在子線程完成運行以前,這個子線程的父線程將一直被阻塞
setDaemon(True):
將線程聲明爲守護線程,必須在start()方法調用以前設置,若是不設置爲守護線,程序會被無限掛起.這個方法基本和join是相反的
當咱們在程序運行中,執行一個主線程,若是主線程又建立一個子線程,主線程和子線程就分兵兩路,分別運行,那麼當主線程完成,想退出時,會校驗子線程是否完成.若是子線程未完成,則主線程會等子線程完成後再退出.但有時後咱們須要的只是主線程
完成了,無論子線程是否完成,都要和主線程一塊兒退出,這時就能夠用setDaemon方法了
1 import threading 2 from time import ctime,sleep 3 import time 4 5 def ListenMusic(name): 6 7 print ("Begin listening to %s. %s" %(name,ctime())) 8 sleep(3) 9 print("end listening %s"%ctime()) 10 11 def RecordBlog(title): 12 13 print ("Begin recording the %s! %s" %(title,ctime())) 14 sleep(5) 15 print('end recording %s'%ctime()) 16 17 18 threads = [] 19 20 21 t1 = threading.Thread(target=ListenMusic,args=('水手',)) 22 t2 = threading.Thread(target=RecordBlog,args=('python線程',)) 23 24 threads.append(t1) 25 threads.append(t2) 26 27 if __name__ == '__main__': 28 29 for t in threads: 30 #t.setDaemon(True) #注意:必定在start以前設置 31 t.start() 32 # t.join() 33 # t1.join() 34 t1.setDaemon(True) 35 36 #t2.join()########考慮這三種join位置下的結果? 37 print ("all over %s" %ctime())
1 import threading 2 from time import ctime,sleep 3 import time 4 5 def ListenMusic(name): 6 7 print ("Begin listening to %s. %s" %(name,ctime())) 8 sleep(3) 9 print("end listening %s"%ctime()) 10 11 def RecordBlog(title): 12 13 print ("Begin recording the %s! %s" %(title,ctime())) 14 sleep(5) 15 print('end recording %s'%ctime()) 16 17 18 threads = [] 19 20 21 t1 = threading.Thread(target=ListenMusic,args=('水手',)) 22 t2 = threading.Thread(target=RecordBlog,args=('python線程',)) 23 24 threads.append(t1) 25 threads.append(t2) 26 27 if __name__ == '__main__': 28 29 for t in threads: 30 #t.setDaemon(True) #注意:必定在start以前設置 31 t.start() 32 # t.join() 33 # t1.join() 34 t1.setDaemon(True) 35 36 #t2.join()########考慮這三種join位置下的結果? 37 print ("all over %s" %ctime())
其餘方法:
# run(): 線程被cpu調度後自動執行線程對象的run方法
# start():啓動線程活動。 # isAlive(): 返回線程是否活動的。 # getName(): 返回線程名。 # setName(): 設置線程名。
threading模塊提供的一些方法: # threading.currentThread(): 返回當前的線程變量。 # threading.enumerate(): 返回一個包含正在運行的線程的list。正在運行指線程啓動後、結束前,不包括啓動前和終止後的線程。 # threading.activeCount(): 返回正在運行的線程數量,與len(threading.enumerate())有相同的結果。
1 import threading 2 import time 3 4 5 6 def sub(): 7 global num 8 #num -= 1 #直接-1從新賦值給num,之間處理速度至關快,還沒等到CPU時間輪循,該線程已處理結束, 9 lock.acquire() #用同步鎖可將下列過程綁定在一塊兒,即等待時間CPU也不會切換 10 temp = num 11 time.sleep(0.000000000000000000000000001) 12 num = temp - 1 13 lock.release() 14 num = 100 15 lock = threading.Lock() #生成一個同步鎖變量 16 l = [] 17 for i in range(100): 18 t = threading.Thread(target=sub) 19 t.start() 20 l.append(t) 21 for m in l: 22 m.join() 23 24 print(num)
因爲多個線程都在操做同一個資源,同步鎖可保證資源不被破壞。
線程同步可以保證多個線程安全訪問競爭資源,最簡單的同步機制是引入互斥鎖。互斥鎖爲資 源引入一個狀態:鎖定/非鎖定。某個線程要更改共享數據時,先將其鎖定,此時資源的狀態 爲「鎖定」,其餘線程不能更改;直到該線程釋放資源,將資源的狀態變成「非鎖定」,其餘的線 程才能再次鎖定該資源。互斥鎖保證了每次只有一個線程進行寫入操做,從而保證了多線程情 況下數據的正確性。
在線程間共享多個資源的時候,若是兩個線程分別佔有一部分資源而且同時等待對方的資源,就會形成死鎖,由於系統判斷這部分資源都在使用,因此這兩個線程在外力做用下將一直等待下去
1 死鎖 2 import threading 3 import time 4 5 6 class Mythread(threading.Thread): 7 def actionA(self): 8 r_lock.acquire() 9 print(self.name, "gotA", time.ctime()) 10 time.sleep(2) 11 r_lock.acquire() 12 13 print(self.name, "gotB", time.ctime()) 14 time.sleep(1) 15 r_lock.release() 16 r_lock.release() 17 18 def actionB(self): 19 r_lock.acquire() 20 print(self.name, "gotB", time.ctime()) 21 time.sleep(2) 22 r_lock.acquire() 23 24 print(self.name, "gotA", time.ctime()) 25 time.sleep(1) 26 A.release() 27 B.release() 28 29 def run(self): 30 self.actionA() 31 self.actionB()
1 #遞歸鎖 2 import threading 3 import time 4 5 6 class Mythread(threading.Thread): 7 def actionA(self): 8 r_lock.acquire() 9 print(self.name, "gotA", time.ctime()) 10 time.sleep(2) 11 r_lock.acquire() 12 13 print(self.name, "gotB", time.ctime()) 14 time.sleep(1) 15 r_lock.release() 16 r_lock.release() 17 18 def actionB(self): 19 r_lock.acquire() 20 print(self.name, "gotB", time.ctime()) 21 time.sleep(2) 22 r_lock.acquire() 23 24 print(self.name, "gotA", time.ctime()) 25 time.sleep(1) 26 r_lock.release() 27 r_lock.release() 28 29 def run(self): 30 self.actionA() 31 self.actionB() 32 33 34 if __name__ == '__main__': 35 r_lock = threading.RLock() #遞歸鎖,內部有一個count計數器,每加一次鎖count+1,每減一次count-1,當count>0時,其餘線程沒法得到 36 l = [] 37 for i in range(5): 38 t = Mythread() 39 t.start() 40 l.append(t) 41 for v in l: 42 v.join()
爲了支持在同一線程中屢次請求同一資源,python提供了"可重入鎖":
threading.RLock。RLock內部維護着一個Lock和一個counter變量,counter記錄了acquire的 次數,從而使得資源能夠被屢次acquire。直到一個線程全部的acquire都被release,其餘的線 程才能得到資源.
event.wait():等待同步
event.set():設置同步
event.clear():清除同步
1 import threading,time 2 class Boss(threading.Thread): 3 def run(self): 4 print("BOSS:今晚你們都要加班到22:00。") 5 print(event.isSet()) 6 event.set() 7 time.sleep(5) 8 print("BOSS:<22:00>能夠下班了。") 9 print(event.isSet()) 10 event.set() 11 class Worker(threading.Thread): 12 def run(self): 13 event.wait() 14 print("Worker:哎……命苦啊!") 15 time.sleep(1) 16 event.clear() 17 event.wait() 18 print("Worker:OhYeah!") 19 if __name__=="__main__": 20 event=threading.Event() 21 threads=[] 22 for i in range(5): 23 threads.append(Worker()) 24 threads.append(Boss()) 25 for t in threads: 26 t.start() 27 for t in threads: 28 t.join()
信號量用來控制線程併發數的,BoundedSemaphore或Semaphore管理一個內置的計數 器,每當調用acquire()時-1,調用release()時+1。
計數器不能小於0,當計數器爲 0時,acquire()將阻塞線程至同步鎖定狀態,直到其餘線程調用release()。(相似於停車位的概念)
BoundedSemaphore與Semaphore的惟一區別在於前者將在調用release()時檢查計數 器的值是否超過了計數器的初始值,若是超過了將拋出一個異常。
1 import threading,time 2 class myThread(threading.Thread): 3 def run(self): 4 if semaphore.acquire(): 5 print(self.name) 6 time.sleep(5) 7 semaphore.release() 8 if __name__=="__main__": 9 semaphore=threading.Semaphore(5) #參數表示容量個數 10 thrs=[] 11 for i in range(100): 12 thrs.append(myThread()) 13 for t in thrs: 14 t.start()
建立一個"隊列"對象
import Queue
q = Queue.Queue(maxsize=10)
Queue.Queue類便是一個隊列的同步實現.隊列長度可爲無限或者有限.可經過Queue的構造函數的可選參數maxsize來設定隊列長度.若是maxsize小於就表示隊列長度無限
將一個值放入隊列中
q.put(10)
調用隊列對象的put()方法在隊尾插入一個項目.put()有兩個參數,第一個item爲必需的,爲插入項目的值;第二個block爲可選參數,默認爲1.
若是隊列當前爲空且block爲1,put()方法就使調用線程暫停,直到空出一個數據單元.若是block爲0,put方法將引起Full異常
將一個值從隊列中取出
q.get()
調用隊列對象的get()方法從隊頭刪除並返回一個項目.可選參數爲block,默認爲True.若是隊列爲空且block爲True,
get()就使調用線程暫停,直至又項目可用.若是隊列爲空且block爲Flase,隊列引起Empty異常
python Queue模塊有三種隊列及構造函數:
1,python Queue模塊的FIFO隊列先進先出. class queue.Queue(maxsize)
2,LIFO相似於堆,即先進後出. class queue.LifoQueue(maxsize)
3,還有一種是優先級別越低越先出來 cass queue.PriorityQueue(maxsize)
此包中的經常使用方法(q = Queue.Queue()): q.qsize() 返回隊列的大小 q.empty() 若是隊列爲空,返回True,反之False q.full() 若是隊列滿了,返回True,反之False q.full 與 maxsize 大小對應 q.get([block[, timeout]]) 獲取隊列,timeout等待時間 q.get_nowait() 至關q.get(False) 非阻塞 q.put(item) 寫入隊列,timeout等待時間 q.put_nowait(item) 至關q.put(item, False) q.task_done() 在完成一項工做以後,q.task_done() 函數向任務已經完成的隊列發送一個信號 q.join() 實際上意味着等到隊列爲空,再執行別的操做
1 import queue #線程隊列 2 3 q = queue.Queue(3) #生成先進先出隊列,參數表明隊列限定寬度 4 # q = queue.LifoQueue #生成先進後出隊列 5 # q = queue.PriorityQueue #生成優先級的隊列 6 q.put("hello") 7 q.put(123) 8 q.put("world") 9 q.put_nowait(44) # 至關於q.put(block=False) 10 #q.put(5,block=False) #當隊列滿會報錯 11 print(q.qsize()) #獲取實際列表寬度 12 print(q.empty()) #判斷列表是否爲空 13 print(q.full()) #判斷列表是否滿 14 while 1: 15 t = q.get() #當列表爲空時,會一直處於獲取狀態 16 #t = q.get_nowait() #至關於 q.get(block=False) 17 #t = q.get(block=False) #當列表爲空時會報錯 18 print(t)
線程和進程和協程的區別
首先咱們來講一下進程、線程還有協程它們三個的區別?
進程做爲操做系統資源分配的最小單位,能夠在進程和進程之間進行數據隔離,若是有特殊須要經過Manager進行進程間的數據共享,而線程纔是執行程序,與CPU進行交互的最小單位,一個CPU同時只能與一個線程進行交互,因此CPU個數與線程數相等才能發揮多線程最大的優點,可是遺憾的時Python中覺得有GIL鎖的存在,並不能這麼作,GIL鎖的存在限制了在同一時間一個進程中只能有一個線程與CPU進行交互,可是這並不影響Python多線程進行IO操做的效率,由於IO操做並不佔用CPU,若是是計算密集型,Python就沒有辦法了,只能是用多進程來提高程序的效率了.再說協程,協程在真正意義上並非真實存在的,經過gevent框架利來實現協程,主要是爲了解決阻塞問題,能夠發揮線程的最大效率,沒必要讓線程發出請求後傻傻的等,原理是讓對一個線程進行分片,在執行是遇到IO操做就直接調用另外一個模塊greenlet的switch方法來切換代碼塊.實現高效率的多線程,不僅有gevent能夠實現協程,還有Twisted,這兩個的底層原理都是基於時間循環實現的異步非阻塞框架.