GIL (global interpreter lock)全局解釋器鎖
基於cpython的介紹:
python中的一個線程是對應於c語言中的一個線程,python在早期的時候爲了簡單,會在解釋器上加一把非常大的鎖,允許我們同一時刻只有一個線程運行在一個cpu上執行字節碼。在某種程度上保證了我們的線程是安全的。它無法將多個線程映射到多個cpu上執行,使得我們無法利用多核優勢。
gil使得同一時刻只有一個線程在一個cpu上執行字節碼
python現在正在努力的去掉gil,像pypy是去gil的.因爲cpython當中的許多第三方包使用gil的,所以短期類gil是不太可能被去掉的。
那麼是不是說在一個時刻只有一個線程運行在我們的cpu上,就意味着我們在使用多線程編碼的時候就是安全的呢?就不用去考慮線程之間的同步呢?實際上不是的。因爲gil在某些情況下是會被釋放的。gil的釋放在python2和python3還有區別的。
gil的釋放:
1.gil會根據執行的字節碼行數以及時間片釋放gil
2.gil還會在遇到io操作的時候主動釋放
所以python在io操作的時候,多線程編程就變得非常重要了。
# import dis
# def add(a):
# a=a+1
# return a
# #我們可以對它進行反編譯的
# print(dis.dis(add))
'''
H:\ANACONDA\python.exe I:/ainlp/pythonHight/chapter11/pythonGIL.py
3 0 LOAD_FAST 0 (a)
2 LOAD_CONST 1 (1)
4 BINARY_ADD
6 STORE_FAST 0 (a)
4 8 LOAD_FAST 0 (a)
10 RETURN_VALUE
None
'''
total=0
def add():
global total
for i in range(1000000):
total+=1
def desc():
global total
for i in range(1000000):
total-=1
import threading
thread1=threading.Thread(target=add)
thread2=threading.Thread(target=desc)
thread1.start()
thread2.start()
thread1.join()
thread2.join()
print(total)
多線程編程
操作系統能夠調度的最小單元是線程,在最開始的時候操作系統能夠調度的最小單元實際上是進程,但是因爲進程對於系統的資源消耗非常大,所以後期就演變出了線程。線程實際上是依賴於進程的。如在windows任務管理器中會看到許多的進程,如下圖:
對於操作系統來說能夠調度的做小單元是線程。
多線程編程:
1.通過Thread類實例化
# 對於io操作來說,多線程和多進程差別不大
import time
import threading
#1.通過Thread類實例化
def get_detail_html(url):
print("get detail html started")
time.sleep(2)
print("get detail html end")
def get_detail_url(url):
print("get detail url started")
time.sleep(4)
print("get detail url end")
if __name__=="__main__":
thread1=threading.Thread(target=get_detail_html,args={"",})
thread2 = threading.Thread(target=get_detail_url, args={"",})
#設置子進程爲守護線程 當主線程退出的時候,子線程也會被關掉。
# thread1.setDaemon(True)
thread2.setDaemon(True)
start_time=time.time()
thread1.start()
thread2.start()
#那麼如何使得在兩個線程運行完成之後再執行主線程呢
thread1.join()
thread2.join()
'''
H:\ANACONDA\python.exe I:/ainlp/pythonHight/chapter11/python_thread.py
get detail html started
get detail url started
get detail html end
get detail url end
last time: 4.0012288093566895
在加入線程阻塞之後,主線程會等待兩個子線程執行完成之後纔開始執行,而兩個子線程
運行所用的時間並不是順序運行的時間,而是在一定程度的並行運行,因此是花費了線程
2所用的時間。
'''
'''
雖然在這個python腳本當中創建了2個線程,但是實際上一共有3個線程
第三個線程就是我們的python程序,即所說的主線程。
'''
print("last time: {}".format(time.time()-start_time))
2.通過繼承Thread來實現多線程 適用於複雜的場景
import time
import threading
class GetDetailHtml(threading.Thread):
#重載init方法
def __init__(self,name):
super().__init__(name=name)
#重載run方法
def run(self):
print("get detail html started")
time.sleep(2)
print("get detail html end")
class GetDetailUrl(threading.Thread):
# 重載init方法
def __init__(self,name):
super().__init__(name=name)
# 重載run方法
def run(self):
print("get detail url started")
time.sleep(4)
print("get detail url end")
#通過這兩個類的實例完成我們的多線程
if __name__=="__main__":
thread1=GetDetailHtml("getdetailhtml")
thread2= GetDetailUrl("getdetailurl")
# thread1.setDaemon(True)
thread2.setDaemon(True)
start_time=time.time()
thread1.start()
thread2.start()
#那麼如何使得在兩個線程運行完成之後再執行主線程呢
thread1.join()
thread2.join()
print("last time: {}".format(time.time() - start_time))
線程間通信
1.線程間共享變量
import time
import threading
from pythonHight.chapter11.variables import detail_url_list
from pythonHight.chapter11 import variables
#1.通過Thread類實例化
#爬取文章詳情頁
def get_detail_html():
detail_url_list=variables.detail_url_list
# while True:
if len(detail_url_list):
# global detail_url_list
url=detail_url_list.pop()
# for url in detail_url_list:
print("get detail html started")
time.sleep(0.5)
print("get detail html end")
#爬取文章列表頁
def get_detail_url():
# global detail_url_list
detail_url_list = variables.detail_url_list
# while True:
print("get detail url started")
time.sleep(1)
for i in range(20):
detail_url_list.append("http://www.baidu.com/{id}".format(id=i))
print("get detail url end")
#1.線程間的通信方式-共享變量
'''
線程間的變量共享涉及到很多的線程安全性問題,因爲gil的原因會導致
線程在處理數據的時候沒有達到預期的效果,可能會造成一定的混亂。
所以並不推薦使用共享變量進行通信,除非大家對鎖比較瞭解
'''
#2.
if __name__=="__main__":
thread_detail_url=threading.Thread(target=get_detail_url,args=())
'''
在真正爬取url列表頁面的時候是很快的,而爬取每個url裏的詳情速度是很慢的
如果這兩個爬取方式只是分別之構建一個線程,則沒有做到高併發,那麼就可以
考慮構建多個爬取詳情頁的線程來增加併發,提高運行效率
'''
'''
直觀的做法:
thread_detail_html1 = threading.Thread(target=get_detail_html, args={"", })
thread_detail_html2 = threading.Thread(target=get_detail_html, args={"", })
thread_detail_html3 = threading.Thread(target=get_detail_html, args={"", })
thread_detail_html4 = threading.Thread(target=get_detail_html, args={"", })
thread_detail_html5 = threading.Thread(target=get_detail_html, args={"", })
thread2 = threading.Thread(target=get_detail_url, args={"",})
thread_detail_url.start()
thread_detail_html1.start()
thread_detail_html2.start()
thread_detail_html3.start()
thread_detail_html4.start()
thread_detail_html5.start()
thread_detail_url.join()
thread_detail_html1.join()
thread_detail_html2.join()
thread_detail_html3.join()
thread_detail_html4.join()
thread_detail_html5.join()
'''
#其實可以使用循環來做
for i in range(10):
detail_html_thread=threading.Thread(target=get_detail_html,args=())
#設置子進程爲守護線程 當主線程退出的時候,子線程也會被關掉。
# thread_detail_url.setDaemon(True)
# detail_html_thread.setDaemon(True)
# start_time=time.time()
thread_detail_url.start()
detail_html_thread.start()
#那麼如何使得在兩個線程運行完成之後再執行主線程呢
thread_detail_url.join()
detail_html_thread.join()
2.通過queue進行線程間同步
# 示意代碼,不可運行。
#通過queue的方式來進行線程間的同步的
import time
import threading
from queue import Queue
#這裏是線程安全的,爲什麼呢,是因爲queue本身就是一個線程安全的
#在queue.get()在進行get操作的時候,他不會使得我們多個我線程去、
#操作同一個queue的時候造成數據的錯誤
'''
Queue是如何做到線程安全的呢,如裏面的get方法是線程安全的,底層用到了
deque雙端隊列,它是線程安全的
'''
# 爬取文章詳情頁
def get_detail_html(queue):
#get方法是個阻塞的方法 如果隊列爲空,他會一直停在這
url = queue.get()
# for url in detail_url_list:
print("get detail html started")
time.sleep(0.5)
print("get detail html end")
# 爬取文章列表頁
def get_detail_url():
while True:
print("get detail url started")
time.sleep(1)
for i in range(20):
Queue.put("http://www.baidu.com/{id}".format(id=i))
print("get detail url end")
if __name__ == "__main__":
detail_url_queue=Queue(maxsize=1000)
thread_detail_url = threading.Thread(target=get_detail_url, args=(detail_url_queue,))
for i in range(10):
detail_html_thread = threading.Thread(target=get_detail_html, args=(detail_url_queue,))
# 設置子進程爲守護線程 當主線程退出的時候,子線程也會被關掉。
# thread_detail_url.setDaemon(True)
# detail_html_thread.setDaemon(True)
# start_time=time.time()
thread_detail_url.start()
detail_html_thread.start()
# 那麼如何使得在兩個線程運行完成之後再執行主線程呢
# thread_detail_url.join()
# detail_html_thread.join()
detail_url_queue.task_done()
detail_url_queue.join()
線程同步(Lock,RLock,Semaphores、Condition)
python中爲什麼需要線程同步,以及同步是什麼意思?
import threading
from threading import Lock
total=0
lock=Lock()
def add():
global total
global lock
for i in range(1000000):
#獲取一把鎖
lock.acquire()
lock.acquire()
#不能在一把鎖沒有釋放的情況下,又獲取鎖
#造成死鎖
total+=1
#釋放鎖
lock.release()
def desc():
global total
for i in range(1000000):
#獲取一把鎖
lock.acquire()
total-=1
#釋放鎖
lock.release()
thread1=threading.Thread(target=add)
thread2=threading.Thread(target=desc)
thread1.start()
thread2.start()
'''
線程同步機制:
1.
'''
# def add1(a):
# a+=1
# def desc1(a):
# a-=1
# import dis
# print(dis.dis(add1))
# print(dis.dis(desc1))
'''
add
1.load a a=0
2.load 1 1
3.+ 1
4.賦值給a 1
desc
1.load a a=0
2.load 1
3.- -1
4.賦值給a -1
在這個字節碼過程當中的任意一個過程,
線程都有可能被切換出去給另一個線程.
使用了鎖之後,多線程之間誰先獲取鎖,那麼這個線程就
會先執行自己的代碼,並且如果這個線程的鎖如果沒有在
執行後得到釋放,那麼所有其他所有線程都會被卡住。
1.使用了鎖之後會影響我們的性能,獲取鎖和釋放鎖都需
要時間。 這是鎖會影響併發性能的必然問題。
2.用鎖會引起死鎖。非常容易引起死鎖。(小心)
死鎖的情況:A(a,b)
A(a,b)
首先 acquire (a)
acquire (b)
B(a,b)
acquire (b)
acquire(a)
這種情況下造成鎖資源的競爭。
當A線程在和B先程
'''
thread1.join()
thread2.join()
print(total)
#RLock在同一個線程裏面,可以連續調用多次acquire,
#一定要注意acquire的次數要和release的次數相等
Condition :
在使用從第同之前先使用lock實驗下面的場景,達不到說一句回答一句的
聊天效果。實例代碼如下:
from threading import Condition
#Condition條件變量,用於複雜的線程間同步的鎖。
from threading import Lock
import threading
class XiaoAi(threading.Thread):
def __init__(self,lock):
self.lock=lock
super().__init__(name="小愛")
def run(self):
self.lock.acquire()
print("{}: 在".format(self.name))
self.lock.release()
self.lock.acquire()
print("{}: 好啊。".format(self.name))
self.lock.release()
pass
class TianMaoJingLing(threading.Thread):
def __init__(self,lock):
self.lock=lock
super().__init__(name="天貓精靈")
def run(self):
self.lock.acquire()
print("{}: 小愛同學".format(self.name))
self.lock.release()
self.lock.acquire()
print("{}: 我們來對古詩吧".format(self.name))
self.lock.release()
if __name__=="__main__":
lock=Lock()
xiaoai=XiaoAi(lock)
tianmao=TianMaoJingLing(lock)
tianmao.start()
xiaoai.start()
使用Condition來實現聊天場景
from threading import Condition
#Condition條件變量,用於複雜的線程間同步的鎖。
from threading import Lock
import threading
# class XiaoAi(threading.Thread):
# def __init__(self,lock):
# self.lock=lock
#
# super().__init__(name="小愛")
# def run(self):
# self.lock.acquire()
# print("{}: 在".format(self.name))
# self.lock.release()
# self.lock.acquire()
# print("{}: 好啊。".format(self.name))
# self.lock.release()
# pass
# class TianMaoJingLing(threading.Thread):
# def __init__(self,lock):
# self.lock=lock
# super().__init__(name="天貓精靈")
# def run(self):
# self.lock.acquire()
# print("{}: 小愛同學".format(self.name))
# self.lock.release()
# self.lock.acquire()
# print("{}: 我們來對古詩吧".format(self.name))
# self.lock.release()
# if __name__=="__main__":
# lock=Lock()
# xiaoai=XiaoAi(lock)
# tianmao=TianMaoJingLing(lock)
# tianmao.start()
# xiaoai.start()
from threading import Condition
#通過condition完成協同對話
class XiaoAi(threading.Thread):
def __init__(self,cond):
self.cond=cond
super().__init__(name="小愛")
def run(self):
# with self.cond:
self.cond.acquire()
#等待通知
self.cond.wait()
print("{}: 在".format(self.name))
#通知天貓精靈
self.cond.notify()
#等待通知
self.cond.wait()
print("{}: 好啊.".format(self.name))
#通知天貓精靈
self.cond.notify()
#等待通知
self.cond.wait()
print("{}: 君住長江尾.".format(self.name))
#通知天貓精靈
self.cond.notify()
#等待通知
self.cond.wait()
print("{}: 共飲長江水.".format(self.name))
#通知天貓精靈
self.cond.notify()
#等待通知
self.cond.wait()
print("{}: 此恨何時已.".format(self.name))
#通知天貓精靈
self.cond.notify()
#等待通知
self.cond.wait()
print("{}: 定不負相思憶.".format(self.name))
#通知天貓精靈
self.cond.notify()
self.cond.release()
class TianMaoJingLing(threading.Thread):
def __init__(self,cond):
self.cond=cond
super().__init__(name="天貓精靈")
def run(self):
with self.cond:
print("{}: 小愛同學。".format(self.name))
#通知小愛同學
self.cond.notify()
#進入等待的狀態 等待小愛的通知
self.cond.wait()
print("{}: 我們來對古詩吧。".format(self.name))
#通知小愛同學
self.cond.notify()
#進入等待的狀態 等待小愛的通知
self.cond.wait()
print("{}: 我在長江頭。".format(self.name))
#通知小愛同學
self.cond.notify()
#進入等待的狀態 等待小愛的通知
self.cond.wait()
print("{}: 日日思君不見君。".format(self.name))
#通知小愛同學
self.cond.notify()
#進入等待的狀態 等待小愛的通知
self.cond.wait()
print("{}: 此水幾時休。".format(self.name))
#通知小愛同學
self.cond.notify()
#進入等待的狀態 等待小愛的通知
self.cond.wait()
print("{}: 只願君心似我心。".format(self.name))
#通知小愛同學
self.cond.notify()
#進入等待的狀態 等待小愛的通知
self.cond.wait()
if __name__=="__main__":
cond=Condition()
xiaoai=XiaoAi(cond)
tianmao=TianMaoJingLing(cond)
#啓動順序很重要
#在調用with cond 之後才能調用 wait()或者notify()
#condition有兩層鎖一把底層鎖會在線程調用了wait方法
# 的時候釋放掉,上面的鎖會在每次調用wait的時候分配
#一把鎖並放到cond的等待隊列中,等待notify方法的喚醒
xiaoai.start()
tianmao.start()
運行結果如下圖:
wait()函數允許我們等待某個條件變量的通知
semaphore 是用於控制進入數量的鎖
#semphore 是用於控制進入數量的鎖
#文件, 讀,寫,寫一般只是用於一個線程寫,讀可以允許有多個線程讀
#爬蟲
import threading
import time
class HtmlSpider(threading.Thread):
def __init__(self,url,sem):
super().__init__()
self.url=url
self.sem=sem
def run(self):
time.sleep(2)
print("got html text success")
self.sem.release()
class UrlProducer(threading.Thread):
def __init__(self,sem):
super().__init__(name="")
self.sem=sem
def run(self):
for i in range(20):
self.sem.acquire()
html_thread=HtmlSpider("http://www.baidu.com/{}".format(i),self.sem)
html_thread.start()
if __name__=="__main__":
#控制進入某段代碼的線程數量 即每次建立3個線程
sem=threading.Semaphore(3)
url_producer=UrlProducer(sem)
url_producer.start()
'''
輸出結果每次輸出三個結果。
'''
from queue import Queue
concurrent線程池編程
python3.2+
from concurrent import futures
#線程池 爲什麼要線程池
'''
能不能有個線程管理包去管理我們的線程,
第一控制線程進入的數量
在主線程中可以獲取某某一個線程的狀態或者說某一個任務
的狀態以及返回值
當一個線程完成的時候我們主線程能夠立即知道
futures可以讓多線程和多進程編碼接口一致
'''
from concurrent.futures import ThreadPoolExecutor,as_completed,wait,FIRST_COMPLETED
#wait可以讓我們的主線程阻塞
import time
def get_html(times):
time.sleep(times)
print("get page {} success".format(times))
return times
#實例化線程池
executor=ThreadPoolExecutor(max_workers=2)
#通過submit函數提交執行的函數到線程池中 submit是立即返回是非阻塞的
# task1=executor.submit(get_html,(3))
# task2=executor.submit(get_html,2)
'''
在實際的爬蟲當中,我們並不是獲取所有的線程的狀態,
而是要獲取已經成功的task的返回
'''
urls=[3,2,4]
#1批量提交
all_task=[executor.submit(get_html,(url)) for url in urls]
# wait(all_task)
#這樣的話,我們就可以控制主線程等待某件事情的發生之後再繼續執行
wait(all_task,return_when=FIRST_COMPLETED)
print("main")
#通過as_completed獲取成已完成的task的結果1
# for future in as_completed(all_task):
# data=future.result()
# print("get {} page success".format(data))
#2通過executor的map獲取已經完成的task的結果
# for result in executor.map(get_html,urls):
# print("get {} page".format(result))
# #done()用於判定某個任務是否完成
# print(task1.done())
# #取消某一個線程
# print(task2.cancel())
# time.sleep(5)
# #是一個阻塞的方法,它能夠返回一個結果
# print(task1.done())
# #result可以獲取task的執行結果
# print(task1.result())
from concurrent.futures import Future
‘’’
Future == task的返回容器或者未來對象 這一設計理念比較重要
是因爲在submit()返回的對象線程任務所處的
狀態有可能是未完成的,但是有可能在將來某個時候完成
所以說他是我們異步的核心。
多進程
因爲有一把gil鎖的存在,python中的多線程實際上無法利用我們多核的優勢,無法達到並行的優勢,多進程編程就可以利用我們多個cpu併發的這麼個需求去提高我們的運行效率。
多進程 與 多線程:
1.對於耗cpu的操作,使用多進程.
2.對於io的操作,使用多線程.切換進程的代價要高於線程
對於耗費cpu的操作,計算,圖形處理,機器學習的算法,以及流行的比特幣挖礦等等耗費到cpu已經不能夠滿足計算的需求,所以說現在的顯卡里面的GPU它的運算速度是高於CPU的 。 對於耗費cpu的操作,多進程優於多線程。
#多cpu的操作用多進程編程
#1.對於耗cpu的操作,使用多進程。
# 2.對於io的操作,使用多線程.切換進程的代價要高於線程。
'''
多進程和多線程使用concurrent.futures的效率是差不多的。
'''
def fibo(n):
if n<2:
return 1
return fibo(n-1)+fibo(n-2)
# print(fibo(10))#89
# 1.使用多線程
from concurrent.futures import ThreadPoolExecutor,as_completed
import time
# with ThreadPoolExecutor(3) as excutor:
# alltask=[excutor.submit(fibo,(num)) for num in range(25,35)]
# starttime=time.time()
# for future in as_completed(alltask):
# data=future.result()
# print("get numbers results is: {}".format(data))
# print("last time is:{}".format(time.time()-starttime))
'''
89
get numbers results is: 121393
get numbers results is: 196418
get numbers results is: 317811
get numbers results is: 514229
get numbers results is: 832040
get numbers results is: 1346269
get numbers results is: 2178309
get numbers results is: 3524578
get numbers results is: 5702887
get numbers results is: 9227465
last time is:15.730899810791016
'''
#2.多進程
import time
from concurrent.futures import ProcessPoolExecutor
'''
注意:windows多進程編程時一定要將執行的ProcessPoolExecutor代碼放到
if __name__=="__main__":之下,不然會報錯。
'''
if __name__=="__main__":
with ProcessPoolExecutor(3) as excutor:
alltask = [excutor.submit(fibo, (num)) for num in range(25, 45)]
starttime = time.time()
for future in as_completed(alltask):
data = future.result()
print("get numbers results is: {}".format(data))
print("last time is:{}".format(time.time() - starttime))
'''
H:\ANACONDA\python.exe I:/ainlp/pythonHight/chapter11/progress_test.py
get numbers results is: 121393
get numbers results is: 196418
get numbers results is: 317811
get numbers results is: 514229
get numbers results is: 832040
get numbers results is: 1346269
get numbers results is: 2178309
get numbers results is: 3524578
get numbers results is: 5702887
get numbers results is: 9227465
last time is:9.456540822982788
'''
對於io操作來說,多線程優於我們的多進程:
#對於io操作來說,多線程優於我們的多進程
def random_sleep(n):
time.sleep(n)
return n
# if __name__=="__main__":
# with ThreadPoolExecutor(3) as excutor:
# alltask = [excutor.submit(random_sleep, (num)) for num in [2]*30]
# starttime = time.time()
# for future in as_completed(alltask):
# data = future.result()
# print("get numbers results is: {}".format(data))
# print("last time is:{}".format(time.time() - starttime))
'''
H:\ANACONDA\python.exe I:/ainlp/pythonHight/chapter11/progress_test.py
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
last time is:20.00114417076111
'''
if __name__=="__main__":
with ProcessPoolExecutor(3) as excutor:
alltask = [excutor.submit(random_sleep, (num)) for num in [2]*30]
starttime = time.time()
for future in as_completed(alltask):
data = future.result()
print("get numbers results is: {}".format(data))
print("last time is:{}".format(time.time() - starttime))
'''
H:\ANACONDA\python.exe I:/ainlp/pythonHight/chapter11/progress_test.py
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
get numbers results is: 2
last time is:20.690183401107788
'''
其實在計算量不是很大的時候,多線程和多進程性能差別不大。操作系統調用的線程數比進程數要多很多,調用和切換進程會消耗很多的cpu內存,因此在某些情況下使用多線程的性能是不會比多進程差的,而且多線程的穩定性是比多進程好的。
多進程編程:
先看一下這個東西:
import os
import time
#fork只能用於linux/unix上
pid =os.fork()
print("xiaopang")
if pid==0:
print('子進程 {} ,父進程是: {}.'.format(os.getpid(),os.getppid()))
else:
print('我是父進程 : {}'.format(pid))
time.sleep(2)
運行結果:
爲什麼兩個進程的fpid不同呢,這與fork函數的特性有關。fork調用的一個奇妙之處就是它僅僅被調用一次,卻能夠返回兩次,它可能有三種不同的返回值:
1)在父進程中,fork返回新創建子進程的進程ID;
2)在子進程中,fork返回0;
3)如果出現錯誤,fork返回一個負值;
將print("xiaopang ")放在fok()的前面。
import os
import time
#fork只能用於linux/unix上
print("xiaopang")
pid =os.fork()
if pid==0:
print('子進程 {} ,父進程是: {}.'.format(os.getpid(),os.getppid()))
else:
print('我是父進程 : {}'.format(pid))
time.sleep(2)
多進程和進程池
from concurrent.futures import ProcessPoolExecutor
import multiprocessing
#多進程編程
import time
def get_html(n):
time.sleep(n)
print("sub_progress success")
return n
if __name__=="__main__":
# process=multiprocessing.Process(target=get_html,args=(2,))
# print(process.pid)
# process.start()
# print(process.pid)
# process.join()
# print("main progress end")
# #使用進程池
pool=multiprocessing.Pool(multiprocessing.cpu_count())
# result=pool.apply_async(get_html,args=(3,))
# #等待所有任務完成
# pool.close()
# pool.join()
# print(result.get())
#imap
# for result in pool.imap(get_html,[1,5,3]):
# print("{} sleep success".format(result))
#imap_unordered
for result in pool.imap_unordered(get_html,[1,5,3]):
print("{} sleep success".format(result))
多進程之間的通信
1使用multiprogressing 中的queue在進程之間進行通信
from multiprocessing import Process,Queue
import time
def producer(queue):
queue.put("a")
time.sleep(2)
def cusumer(queue):
time.sleep(2)
data=queue.get()
print(data)
if __name__=="__main__":
queue=Queue(10)
myproducer=Process(target=producer,args=(queue,))
mycusumer=Process(target=cusumer,args=(queue,))
myproducer.start()
mycusumer.start()
myproducer.join()
mycusumer.join()
multiprocessing中的queue不能用於進程池的通訊,因爲
不起作用。
#multiprocessing中的queue不能用於進程池的通訊
from multiprocessing import Process,Queue,Pool
import time
def producer(queue):
queue.put("a")
time.sleep(2)
def cusumer(queue):
time.sleep(2)
data=queue.get()
print(data)
if __name__=="__main__":
queue=Queue(10)
pool=Pool(2)
pool.apply_async(producer,args=(queue,))
pool.apply_async(cusumer,args=(queue,))
pool.close()
pool.join()
可以使用Manage之下的queue進行進程池間的通信
from multiprocessing import Process,Queue,Pool,Manager
import time
def producer(queue):
queue.put("a")
time.sleep(2)
def cusumer(queue):
time.sleep(2)
data=queue.get()
print(data)
if __name__=="__main__":
queue=Manager().Queue(10)
pool=Pool(2)
pool.apply_async(producer,args=(queue,))
pool.apply_async(cusumer,args=(queue,))
pool.close()
pool.join()
進程間通訊的方式 管道pipe
pipe的性能高於queue
pipe只能用於兩個進程間的通訊
#進程間通訊的方式 管道pipe
# pipe的性能高於queue
# pipe只能用於兩個進程間的通訊
from multiprocessing import Process,Pipe
#Pipe是簡化版的queue
import time
def producer(pipe):
pipe.send("xiaopang")
def cusumer(pipe):
print(pipe.recv())
if __name__=="__main__":
recevie_pipe,send_pipe=Pipe()
# pipe只能用於兩個進程間的通訊
myproducer=Process(target=producer,args=(send_pipe,))
mycusumer=Process(target=cusumer,args=(recevie_pipe,))
myproducer.start()
mycusumer.start()
myproducer.join()
mycusumer.join()
使用Manage裏的dict進行進程間的共享內存操作,注意數據間的進程同步.
# 使用Manage裏的dict進行進程間的共享內存操作
from multiprocessing import Manager,Process
def add_data(p_dict,key,value):
p_dict[key]=value
if __name__=="__main__":
process_dict=Manager().dict()
first_Process=Process(target=add_data,args=(process_dict,"xiaopang ",23))
second_process=Process(target=add_data,args=(process_dict,"xiaohe",26))
first_Process.start()
second_process.start()
first_Process.join()
second_process.join()
print(process_dict)
完結
下一篇: 協程和異步io