一绅你、多進(jìn)程
1昭躺、子進(jìn)程(subprocess包)
在python中领炫,通過subprocess包,fork一個子進(jìn)程帝洪,并運行外部程序。
import subprocess
child1 = subprocess.Popen(["ls","-l"], stdout=subprocess.PIPE)
child2 = subprocess.Popen(["wc"], stdin=child1.stdout,stdout=subprocess.PIPE)
out = child2.communicate()
print(out)
subprocess參考
從subprocess運行的子進(jìn)程中實時獲取輸出
2啄枕、多進(jìn)程(multiprocessing包)
它可以利用multiprocessing.Process對象來創(chuàng)建一個進(jìn)程族沃。
進(jìn)程池 (Process Pool)可以創(chuàng)建多個進(jìn)程泌参。
apply_async(func,args) 從進(jìn)程池中取出一個進(jìn)程執(zhí)行func,args為func的參數(shù)盖溺。它將返回一個AsyncResult的對象铣缠,你可以對該對象調(diào)用get()方法以獲得結(jié)果。
close() 進(jìn)程池不再創(chuàng)建新的進(jìn)程
join()方法可以等待子進(jìn)程結(jié)束后再繼續(xù)往下運行蝇庭,通常用于進(jìn)程間的同步捡硅。
join() wait進(jìn)程池中的全部進(jìn)程。必須對Pool先調(diào)用close()方法才能join北发。
#! /usr/bin/env python
# -*- coding:utf-8 -*-
# __author__ == "111"
# "我的電腦有4個cpu"
from multiprocessing import Pool
import os, time
def long_time_task(name):
print 'Run task %s (%s)...' % (name, os.getpid())
start = time.time()
time.sleep(3)
end = time.time()
print 'Task %s runs %0.2f seconds.' % (name, (end - start))
if __name__=='__main__':
print 'Parent process %s.' % os.getpid()
p = Pool()
for i in range(4):
p.apply_async(long_time_task, args=(i,))
print 'Waiting for all subprocesses done...'
p.close()
p.join()
print 'All subprocesses done.'
多進(jìn)程共享資源
通過共享內(nèi)存和Manager對象:用一個進(jìn)程作為服務(wù)器喷屋,建立Manager來真正存放資源。
其它的進(jìn)程可以通過參數(shù)傳遞或者根據(jù)地址來訪問Manager狱庇,建立連接后,操作服務(wù)器上的資源陕截。
#! /usr/bin/env python
# -*- coding:utf-8 -*-
# __author__ == "111"
from multiprocessing import Queue,Pool
import multiprocessing,time,random
def write(q):
for value in ['A','B','C','D']:
print "Put %s to Queue!" % value
q.put(value)
time.sleep(random.random())
def read(q,lock):
while True:
lock.acquire()
if not q.empty():
value=q.get(True)
print "Get %s from Queue" % value
time.sleep(random.random())
else:
break
lock.release()
if __name__ == "__main__":
manager=multiprocessing.Manager()
q=manager.Queue()
p=Pool()
lock=manager.Lock()
pw=p.apply_async(write,args=(q,))
pr=p.apply_async(read,args=(q,lock))
p.close()
p.join()
print ("所有數(shù)據(jù)都寫入并且讀完")
二批什、多線程
threading包
import time, threading
# 新線程執(zhí)行的代碼:
def loop():
print('thread %s is running...' % threading.current_thread().name)
n = 0
while n < 5:
n = n + 1
print('thread %s >>> %s' % (threading.current_thread().name, n))
time.sleep(1)
print('thread %s ended.' % threading.current_thread().name)
print('thread %s is running...' % threading.current_thread().name)
t = threading.Thread(target=loop, name='LoopThread')
t.start()
t.join()
print('thread %s ended.' % threading.current_thread().name)
python的多線程在同一時刻只會有一條線程跑在CPU里面驻债,其他線程都在睡覺。這個就是因為傳說中的GIL(全局解釋鎖)的存在暮的。任何Python線程執(zhí)行前淌实,必須先獲得GIL鎖.
那python多線程還有用處嗎?當(dāng)然拆祈!
如果是一個計算為主的程序(專業(yè)一點稱為CPU密集型程序),這一點確實是比較吃虧的咙咽,每個線程運行一遍淤年,就相當(dāng)于單線程在跑,甚至比單線程還要慢——CPU切換線程的上下文也是要有開銷的溉苛。
辣雞
如果是一個磁盤或網(wǎng)絡(luò)為主的程序(IO密集型)就不同了豹休。一個線程處在IO等待的時候,另一個線程還可以在CPU里面跑凤巨,有時候CPU閑著沒事干洛搀,所有的線程都在等著IO,這時候他們就是同時的了留美,而單線程的話此時還是在一個一個等待的。我們都知道IO的速度比起CPU來是慢到令人發(fā)指的逢倍,python的多線程就在這時候發(fā)揮作用了。比方說多線程網(wǎng)絡(luò)傳輸碉哑,多線程往不同的目錄寫文件亮蒋,等等。
線程同步
多線程中贮尖,所有變量都由所有線程共享趁怔,所以,任何一個變量都可以被任何一個線程修改痕钢,因此任连,線程之間共享數(shù)據(jù)最大的危險在于多個線程同時改一個變量例诀,把內(nèi)容給改亂了.
一個線程使用自己的局部變量比使用全局變量好,因為局部變量只有線程自己能看見繁涂,不會影響其他線程,而全局變量的修改必須加鎖秉沼。
import time, threading
# 假定這是你的銀行存款:
balance = 0
def change_it(n):
# 先存后取矿酵,結(jié)果應(yīng)該為0:
global balance
balance = balance + n
balance = balance - n
def run_thread(n):
for i in range(100000):
change_it(n)
t1 = threading.Thread(target=run_thread, args=(5,))
t2 = threading.Thread(target=run_thread, args=(8,))
t1.start()
t2.start()
t1.join()
t2.join()
print(balance)
解決:加鎖
balance = 0
lock = threading.Lock()
def run_thread(n):
for i in range(100000):
# 先要獲取鎖:
lock.acquire()
try:
# 放心地改吧:
change_it(n)
finally:
# 改完了一定要釋放鎖:
lock.release()
三全肮、協(xié)程
協(xié)程,又稱微線程辜腺,纖程乍恐。英文名Coroutine测砂。一句話說明什么是線程:協(xié)程是一種用戶態(tài)的輕量級線程。
線程是系統(tǒng)級別的砌些,它們是由操作系統(tǒng)調(diào)度;協(xié)程是程序級別的宙彪,由程序員根據(jù)需要自己調(diào)度有巧。我們把一個線程中的一個個函數(shù)叫做子程序,那么子程序在執(zhí)行過程中可以中斷去執(zhí)行別的子程序男图;別的子程序也可以中斷回來繼續(xù)執(zhí)行之前的子程序甜橱,這就是協(xié)程。也就是說同一線程下的一段代碼<1>執(zhí)行著執(zhí)行著就可以中斷难裆,然后跳去執(zhí)行另一段代碼镊掖,當(dāng)再次回來執(zhí)行代碼塊<1>的時候,接著從之前中斷的地方開始執(zhí)行症虑。
協(xié)程的優(yōu)點:
(1)無需線程上下文切換的開銷归薛,協(xié)程避免了無意義的調(diào)度,由此可以提高性能(但也因此主籍,程序員必須自己承擔(dān)調(diào)度的責(zé)任,同時沈条,協(xié)程也失去了標(biāo)準(zhǔn)線程使用多CPU的能力)
(2)無需原子操作鎖定及同步的開銷
(3)方便切換控制流诅炉,簡化編程模型
(4)高并發(fā)+高擴(kuò)展性+低成本:一個CPU支持上萬的協(xié)程都不是問題屋厘。所以很適合用于高并發(fā)處理汗洒。
協(xié)程的缺點:
(1)無法利用多核資源:協(xié)程的本質(zhì)是個單線程,它不能同時將 單個CPU 的多個核用上,協(xié)程需要和進(jìn)程配合才能運行在多CPU上.當(dāng)然我們?nèi)粘K帉懙慕^大部分應(yīng)用都沒有這個必要父款,除非是cpu密集型應(yīng)用。
(2)進(jìn)行阻塞(Blocking)操作(如IO時)會阻塞掉整個程序
參考:
https://www.cnblogs.com/zingp/p/5911537.html
https://blog.csdn.net/andybegin/article/details/77884645
四世杀、異步
無論是線程還是進(jìn)程肝集,使用的都是同步進(jìn)制,當(dāng)發(fā)生阻塞時所刀,性能會大幅度降低捞挥,無法充分利用CPU潛力,浪費硬件投資砌函,更重要造成軟件模塊的鐵板化胸嘴,緊耦合,無法切割劣像,不利于日后擴(kuò)展和變化摧玫。
不管是進(jìn)程還是線程诬像,每次阻塞、切換都需要陷入系統(tǒng)調(diào)用(system call)坏挠,先讓CPU跑操作系統(tǒng)的調(diào)度程序降狠,然后再由調(diào)度程序決定該跑哪一個進(jìn)程(線程)庇楞。多個線程之間在一些訪問互斥的代碼時還需要加上鎖否纬,
現(xiàn)下流行的異步server都是基于事件驅(qū)動的(如nginx)。
異步事件驅(qū)動模型中睛驳,把會導(dǎo)致阻塞的操作轉(zhuǎn)化為一個異步操作膜廊,主線程負(fù)責(zé)發(fā)起這個異步操作,并處理這個異步操作的結(jié)果蹬跃。由于所有阻塞的操作都轉(zhuǎn)化為異步操作钥勋,理論上主線程的大部分時間都是在處理實際的計算任務(wù),少了多線程的調(diào)度時間算灸,所以這種模型的性能通常會比較好菲驴。