[英]How to share objects and data between python processes in real-time?
我試圖在 Python 中為實時應用程序、多處理和大文件找到一種合理的方法。
一個父進程產生 2 個或更多的子進程。 第一個孩子讀取數據,保存在內存中,其他孩子以管道方式處理它。 數據應該組織成一個對象,發送到下面的流程,處理,發送,處理等等。
由於開銷(序列化等),可用的方法(例如管道、隊列、管理器)似乎不夠用。
有沒有足夠的方法來解決這個問題?
我已經在高內存應用程序中使用 Celery 和 Redis 進行實時多處理,但這實際上取決於您要完成的任務。
我在 Celery 中發現的與內置多處理工具(管道/隊列)相比的最大好處是:
為了真正提高性能,ZMQ 是我的首選。 設置和微調還有很多工作要做,但它與您可以安全獲得的裸套接字一樣接近。
免責聲明:這都是軼事。 這真的歸結為您的具體需求。 在你走上任何道路之前,我會用示例數據對不同的選項進行基准測試。
首先,由於所有開銷而懷疑消息傳遞可能不充分並不是使程序過於復雜的好理由。 這是構建概念驗證並提出一些示例數據並開始測試的一個很好的理由。 如果您花費 80% 的時間腌制東西或通過隊列推送東西,那么是的,這可能會成為您現實生活中的代碼問題-假設您的概念證明所做的工作量與您的實際工作量相當代碼。 但如果你把 98% 的時間都花在真正的工作上,那么就沒有問題需要解決。 消息傳遞會更簡單,所以只需使用它。
此外,即使您確實在這里發現了問題,也並不意味着您必須放棄消息傳遞; 這可能只是multiprocessing
內置內容的問題。 像 0MQ 和 Celery 這樣的技術可能比簡單的隊列具有更低的開銷。 即使對通過隊列發送的內容更加小心,也會產生巨大的不同。
但如果消息傳遞結束,顯而易見的替代方案是數據共享。 這在multiprocessing
文檔中得到了很好的解釋,以及每個文檔的優缺點。
在進程之間共享狀態描述了如何做到這一點的基礎知識。 還有其他替代方法,例如使用特定於平台的共享內存 API 的mmap
ped 文件,但沒有太多理由在multiprocessing
上這樣做,除非您需要,例如,在運行之間的持久存儲。
有兩個大問題需要處理,但都可以處理。
首先,您不能共享 Python 對象,只能共享簡單的值。 Python 對象之間到處都有內部引用,垃圾收集器無法看到對其他進程堆中對象的引用,等等。 所以multiprocessing.Value
只能保存與array.array
相同的基本類型的原生值,而multiprocessing.Array
可以保存(正如您從名稱中猜到的)相同值的一維數組,僅此而已。 對於更復雜的事情,如果您可以根據ctypes.Structure
定義它,則可以使用https://docs.python.org/3/library/multiprocessing.html#module-multiprocessing.sharedctypes
,但這仍然意味着對象之間的任何引用都必須是間接的。 (例如,您通常必須將索引存儲到數組中。)(當然,如果您使用的是 NumPy,這一切都不是壞消息,因為您可能已經將大部分數據存儲在簡單值的 NumPy 數組中,這可以共享。)
其次,共享數據當然會受到競爭條件的影響。 而且,與單個進程中的多線程不同,您不能依賴 GIL 來幫助保護您; 有多個解釋器可以同時嘗試修改相同的數據。 所以你必須使用鎖或條件來保護事物。
對於多處理管道,請查看MPipe 。
對於共享內存(特別是 NumPy 數組),請查看numpy-sharedmem 。
我已經使用這些來執行高性能實時並行圖像處理(使用 OpenCV 進行平均累積和面部檢測),同時從多核 CPU 系統中擠出所有可用資源。 有興趣的可以看看夏洛克。 希望這可以幫助。
一種選擇是使用像Brain-plasma這樣的東西,它維護一個獨立於 Python 進程或線程的共享內存對象命名空間。 有點像 Redis,但可以與大對象一起使用,並且有一個簡單的 API,構建在 Apache Arrow 之上。
$ pip install brain-plasma
# process 1
from brain_plasma import Brain
brain = Brain()
brain['myvar'] = 657
# process 2
from brain_plasma import Brain
brain = Brain()
brain['myvar']
# >>> 657
Python 3.8 現在使用multiprocessing.shared_memory在進程之間提供共享內存訪問。 您在進程之間傳遞的只是一個引用共享內存塊的字符串。 在消費過程中,您將獲得一個支持切片的memoryview
對象,而無需像字節數組那樣復制數據。 如果您使用 numpy,它可以在 O(1) 操作中引用內存塊,從而允許快速傳輸大塊數字數據。 據我所知,通用對象仍然需要反序列化,因為原始字節數組是消費進程收到的。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.