進程:正在運行的程序實例,是對資源管理的集合
線程:系統調度的最小單位,是對執行指令的集合
協程:用戶態的線程,主動的協助式工作
對比:
1、進程不能單獨運行,每個進程創建時都會主動創建一個子線程用來執行具體操作
2、單個進程中可以存在多個子線程,第一個創建的子線程為主線程,線程可以再創線程
3、單個進程中,所有線程都是平等的,共享同一塊內存空間(進程內存空間)
4、啟動線程比啟動進程快,每個進程內存獨立
5、創建子進程相當于克隆一份父進程的內存空間
6、進程間不能直接訪問,除父進程訪問子進程之外,其他進程間相互訪問必須通過中間代理,但是同一進程中的所有線程都可以相互訪問和操作。
一、協程
1、簡介
協程是在一個線程執行過程中可以在一個子程序的預定或者隨機位置中斷,然后轉而執行別的子程序,在適當的時候再返回來接著執行。他本身是一種特殊的子程序或者稱作函數
通過yeild實現的生產消費者模型就是典型的協程。
2、優點:
無須線程上下文切換的開銷
無須數據操作鎖定與同步的開銷
方便切換控制流,簡化編程模型
高并發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題,適用于處理高并發
3、缺點:
無法利用多核資源,協程的本質是個單線程,無法將CPU的多核用上
進行阻塞操作時,會阻塞掉整個程序
4、Greenlet(封裝好的協程模塊)
為了保證并發效果,遇到 IO操作就切換,操作完后切換回來,Greenlet實現了這一功能
greenlet.swith() #主動式的切換
5、Gevent(自動切換)
Gevent封裝了Greenlet并且將手動切換修改為了自動切換
可以輕松的通過Gevent實現并發同步或異步編程
二、多進程(multiprocessing)
p = multiprocessing.Process(target=func, args(i,)) #創建進程
# func函數中必須包含創建線程的操作
1、獲取進程信息
name:調用進程的模塊名稱
os.getppid:父進程ID
os.getpid:子進程ID
2、進程間的數據交互
進程間的內存是獨立的,必須通過中間件進行交互。
提供了三種方法:
隊列(Queue)
import Queue
def func(q):
q.put(value) #將value放入隊列中
q = Queue()
p = multiprocessing.Process(target=func, args(q))
p.start()
print q.get() #取出value
p.join() #等待進程執行結束
看上去像是數據共享,但實際上是克隆了一份Queue交給子進程,子進程將Queue序列化后交給中間件進行反序列化再交給其他進程。
管道(Pipes):
類似socket,建立一個溝通橋梁,兩個進程分別在兩端進行數據交互操作
數據共享(Manager)
import multiprocessing from Manager, Process
with Manager() as manager:
d = manager.dict() #生成一個可供多個進程間共享的字典
3、進程池(Pool)
由于創建子進程時就是克隆一份父進程的內存空間給子進程,操作時內存損耗過大,這時候就需要進程池來對進程數量進行限制。
進程池內部維護著一個序列,存儲多個進程信息,調用進程時向進程池發起請求,如果進程池內沒有可供使用的進程,則等待至有進程響應請求為止。
#方法有 appley(同步、串行) 、apply_async(異步、并行)
import multiprocessing import Pool
pool = Pool(process=num) #num:進程池最大進程數量
for i in range(10):
pool.apply_async(func=func, args=(i), callback=cbk)
#func 進程操作指令,i參數, cbk 回調函數
p.close() #程序結束前必須先關閉進程池,再join等待進程結束
p.join() #進程池中進程執行完畢后退出
四、多線程(threading)
1、調用方法
threading.Thread(target=func, args(i,)) #直接創建線程
class MyThread(threading.Thread) #類繼承創建線程
2、使用for循環可以創建多個線程,每個創建的線程不會等待前一個線程執行完畢,線程是異步、并行的。
3、等待子線程執行結果的方法
隊列(Queue)
作用:增加雙方的效率,完成程序的解耦,松耦合。
隊列是有序的,可以理解為一個存放線程的容器。
#三種形態
queue.Queue(maxsize=n) #FIFO先入先出模式
queue.LifoQueue(maxsize=n) #LIFO先入后出模式
queue.ProiorityQueue(maxsize=n) #存儲時可以指定優先級
#基本方法
q = queue.Queue(10) #實例化隊列對象
q.put() #存放
q.get() #取出
q.size() #查看大小
q.get_nowait() #沒有數據取出時彈出異常
q.put(1, value) #指定優先級,1為優先等級,越小越高
join() 等待線程結束
t_objs = [] #創建用來存放線程的列表
for i in range(10):
t = threading.Thread(target=func, args(i))
t.start()
t_objs.append(t)
print "現在活動的線程總數為:",threading.active_count()
for t in t_objs:
t.join() #t.wait等待線程執行結束后再結束進程
GIL全劇解釋器鎖
python可以創建多個線程并平均分布到計算機的多核中,但是同時只能有一核在處理數據。
GIL全局解釋器鎖保證了數據的統一,在多線程修改數據時,為了防止數據混亂,對多線程進行限制變為串行處理。
線程鎖(threading.Lock互斥鎖)
線程之間相互溝通,并保證同一時間只有一個線程在對數據進行修改
lock = threading.Lock()
lock.acquire() #上鎖,數據修改操作前
lock.release() #解鎖,數據修改操作后
遞歸鎖(threading.RLock)
當設計多線程中存在多個互斥鎖時,解鎖就會混亂,這時候需要遞歸鎖,從最底層鎖一層一層向上解鎖。
信號量(threading.BoundedSemaphore())
互斥鎖只允許同一時間一個線程進行數據修改操作,而Semaphore允許多個。
semaphore = threading.BoundedSemaphore(num) #最多num個
# 上鎖與解鎖與Lock,RLock一致
事件events(threading.Events())
控制線程之間的交互,就是設置一個全局變量,負責線程之間的通信。
events = threading.Events() #聲明
events.set() #設置標志位
events.clean() #清除標志位
events.is_set() #檢測標志位
events.wait() #等待標志位設定后繼續執行
生產消費者模型
在并發編程中,生產消費者模型可以解決大部分的并發問題。
該模式通過平衡生產線和消費線程的工作能力來提高整體的處理速度。
生產消費者模型是通過一個容器來解決生產消費的強耦合問題。
生產者和消費者之間無序彼此通信,而通過阻塞隊列來進行通訊,生產者生產完成后無須等待消費者消費,直接丟給阻塞隊列,消費者請求消費時也無須提醒生產者,直接從阻塞隊列獲取。阻塞隊列形成了一個緩沖區,平衡了生產者和消費者的處理能力。
簡單模型代碼:
import queue
import threading
import time
q = queue.Queue(maxsize=10)
def produce(name):
count = 1
while True:
q.put('饅頭')
print "%s生產饅頭*1"%name
count += 1
time.sleep(1)
def consumer(name):
while True:
print "%s吃掉一個饅頭"%name
time.sleep(2)
p = threading.Thread(target=produce, args=('p01'))
c1 = threading.Thread(target=consumer, args=('c01'))
c2 = threading.Thread(target=consumer, args=('c02'))