1. 程式人生 > >python核心高階學習總結3-------python實現程序的三種方式及其區別

python核心高階學習總結3-------python實現程序的三種方式及其區別

python實現程序的三種方式及其區別

在python中有三種方式用於實現程序 多程序中, 每個程序中所有資料( 包括全域性變數) 都各有擁有⼀份, 互不影響

1.fork()方法

ret = os.fork()
if ret == 0:
   #子程序
else:
   #父程序
這是python中實現程序最底層的方法,其他兩種從根本上也是利用fork()方法來實現的,下面是fork()方法的原理示意圖
getpid()、getppid()方法
import os
rpid = os.fork()
if rpid<0:
    print("fork調⽤失敗。 ")
elif rpid == 0:
    print("我是⼦程序( %s) , 我的⽗程序是(%s) "%(os.getpid(),os.getppid()))
    x+=1
else:
    print("我是⽗程序( %s) , 我的⼦程序是( %s) "%(os.getpid(),rpid))

print("⽗⼦程序都可以執⾏這⾥的程式碼")
執行結果:
我是⽗程序( 19360) , 我的⼦程序是( 19361)
⽗⼦程序都可以執⾏這⾥的程式碼
我是⼦程序( 19361) , 我的⽗程序是( 19360)
⽗⼦程序都可以執⾏這⾥的程式碼
注意: (1)其中os.fork()的返回值ret在第一行執行後,會開闢另外一個子程序,然後父程序跟子程序同時從此句往下執行,對於子程序來說,ret值是0,對於父程序來說ret值是一個大於0的值,這個值實際上就是新開子程序的pid. (2)此種開闢程序的方式不會發生堵塞,也就是父程序結束並不會影響子程序的繼續執行。實際上是根據作業系統的排程演算法來實現的,父子程序相互不影響。 多程序修改全域性變數
#coding=utf-8
import os
import time

num = 0

# 注意, fork函式, 只在Unix/Linux/Mac上運⾏, windows不可以
pid = os.fork()

if pid == 0:
    num+=1
    print('哈哈1---num=%d'%num)
else:
    time.sleep(1)
    num+=1
    print('哈哈2---num=%d'%num)

執行結果

哈哈1---num=1
哈哈2---num=1
#多程序不共享全域性變數
多次fork()問題

結論:多次fork()會有多個程序生成,生成規則同上。
2,Process方法
python是跨平臺的,所以自然肯定會為我們提供實現多程序的庫,畢竟在win裡面用不了fork()。此方法需要匯入對應模組
from multiprocessing import Process
p1=Process(target=xxxx)
p1.start()
這個方法常用場景是使用少量程序做主動服務,如qq客戶端,等這樣的可以開多個。 還可以繼承Process模組並實現run方法來呼叫,此時xxxx方法等價於run方法執行的內容,在重寫過run方法後,在執行子類例項物件的start方法時,會自動呼叫實現的run方法,這個跟java裡面也是類似的。 注意,此種方法子程序不結束,父程序也會堵塞,也就是等子程序都結束後,父程序才會結束,通常應用於程序間的同步。 程式碼實現演示
#coding=utf-8
from multiprocessing import Process
import os

# ⼦程序要執⾏的程式碼
def run_proc(name):
    print('⼦程序運⾏中, name= %s ,pid=%d...' % (name, os.getpid()))

if __name__=='__main__':
    print('⽗程序 %d.' % os.getpid())
    p = Process(target=run_proc, args=('test',))
    print('⼦程序將要執⾏')
    p.start()
    p.join()
    print('⼦程序已結束')
執行結果
⽗程序 4857.
⼦程序將要執⾏
⼦程序運⾏中, name= test ,pid=4858...
⼦程序已結束
Process類常⽤⽅法:
is_alive(): 判斷程序例項是否還在執⾏;
join([timeout]): 是否等待程序例項執⾏結束, 或等待多少秒;
start(): 啟動程序例項( 建立⼦程序) ;
run(): 如果沒有給定target引數, 對這個物件調⽤start()⽅法時, 就將執⾏物件中的run()⽅法;
terminate(): 不管任務是否完成, ⽴即終⽌;
3,利用程序池Pool
當需要建立的⼦程序數量不多時, 可以直接利⽤multiprocessing中的Process動態成⽣多個程序, 但如果是上百甚⾄上千個⽬標, ⼿動的去建立程序的⼯作量巨⼤, 此時就可以⽤到multiprocessing模組提供的Pool⽅法。此方法也需匯入對應模組
from multiprocessing import Pool
pool=Pool(3)
pool.apply_async(xxxx)
xxxx表示要在程序中執行的程式碼塊或方法、函式
此方法可以用來做伺服器端的響應,往往主程序比較少,而Pool()中的引數值,也就是程序池的大小,真正的任務都在子程序中執行。 使用示例
from multiprocessing import Pool
import os,time,random


def worker(msg):
    t_start = time.time()
    print("%s開始執⾏,程序號為%d"%(msg,os.getpid()))
    #random.random()隨機⽣成0~1之間的浮點數
    time.sleep(random.random()*2)
    t_stop = time.time()
    print(msg,"執⾏完畢, 耗時%0.2f"%(t_stop-t_start))

po=Pool(3) #定義⼀個程序池, 最⼤程序數3
for i in range(0,10):
    #Pool.apply_async(要調⽤的⽬標,(傳遞給⽬標的引數元祖,))
    #每次迴圈將會⽤空閒出來的⼦程序去調⽤⽬標
    po.apply_async(worker,(i,))
    print("----start----")
po.close() #關閉程序池, 關閉後po不再接收新的請求
po.join() #等待po中所有⼦程序執⾏完成, 必須放在close語句之後
print("-----end-----")
執行結果
----start----
----start----
0開始執⾏,程序號為5025
----start----
1開始執⾏,程序號為5026
----start----
----start----
----start----
----start----
----start----
----start----
----start----
2開始執⾏,程序號為5027
0 執⾏完畢, 耗時0.58
3開始執⾏,程序號為5025
1 執⾏完畢, 耗時0.70
4開始執⾏,程序號為5026
2 執⾏完畢, 耗時1.36
5開始執⾏,程序號為5027
3 執⾏完畢, 耗時1.03
6開始執⾏,程序號為5025
4 執⾏完畢, 耗時1.12
7開始執⾏,程序號為5026
5 執⾏完畢, 耗時1.25
8開始執⾏,程序號為5027
7 執⾏完畢, 耗時1.28
9開始執⾏,程序號為5026
6 執⾏完畢, 耗時1.91
8 執⾏完畢, 耗時1.23
9 執⾏完畢, 耗時1.38
-----end-----
上面使用的是非堵塞方法,如果使用aply(),則是堵塞方法
from multiprocessing import Pool
import os,time,random


def worker(msg):
    t_start = time.time()
    print("%s開始執⾏,程序號為%d"%(msg,os.getpid()))
    #random.random()隨機⽣成0~1之間的浮點數
    time.sleep(random.random()*2)
    t_stop = time.time()
    print(msg,"執⾏完畢, 耗時%0.2f"%(t_stop-t_start))


po=Pool(3) #定義⼀個程序池, 最⼤程序數3

for i in range(0,10):
    po.apply(worker,(i,))

print("----start----")
po.close() #關閉程序池, 關閉後po不再接收新的請求
po.join() #等待po中所有⼦程序執⾏完成, 必須放在close語句之後
print("-----end-----")
執行結果
0開始執⾏,程序號為5280
0 執⾏完畢, 耗時0.91
1開始執⾏,程序號為5281
1 執⾏完畢, 耗時1.59
2開始執⾏,程序號為5282
2 執⾏完畢, 耗時1.25
3開始執⾏,程序號為5280
3 執⾏完畢, 耗時0.53
4開始執⾏,程序號為5281
4 執⾏完畢, 耗時1.49
5開始執⾏,程序號為5282
5 執⾏完畢, 耗時0.18
6開始執⾏,程序號為5280
6 執⾏完畢, 耗時1.51
7開始執⾏,程序號為5281
7 執⾏完畢, 耗時0.88
8開始執⾏,程序號為5282
8 執⾏完畢, 耗時1.08
9開始執⾏,程序號為5280
9 執⾏完畢, 耗時0.12
----start----
-----end-----
multiprocessing.Pool常⽤函式解析:
apply_async(func[, args[, kwds]])
: 使⽤⾮阻塞⽅式調⽤func( 並⾏執⾏, 堵塞⽅式必須等待上⼀個程序退出才能執⾏下⼀個程序) , args為傳遞給func的引數列表, kwds為傳遞給func的關鍵字引數列表; apply(func[, args[, kwds]]): 使⽤阻塞⽅式調⽤funcclose(): 關閉Pool, 使其不再接受新的任務; terminate(): 不管任務是否完成, ⽴即終⽌; join(): 主程序阻塞, 等待⼦程序的退出, 必須在close或terminate之後使⽤;