簡體   English   中英

創建類似文件的對象作為數據緩沖區

[英]Create file-like object for data buffer

語境化

我正在編寫一個程序,該程序能夠從傳感器讀取數據,然后對其進行處理。 目前,我希望將其發送到服務器。 我有兩個通過套接字進行通信的進程,一個進程讀取數據並將其存儲到一個臨時文件,另一個進程讀取該臨時文件,然后將數據發送到服務器。

問題

該問題實際上從未在測試中出現過,但是我已經意識到,很有可能如果采樣頻率很高,則兩個進程在嘗試同時讀取/寫入文件時會同時發生 (不是他們要求完全相同)同時,但一個嘗試在另一個關閉之前將其打開)。

即使這不會引發錯誤(對於我在網上閱讀的內容,某些操作系統也不會在文件中添加鎖),它也可能導致巨大的版本不兼容錯誤,從而導致數據丟失。 因此,這種處理數據的方式看起來不太合適。

我自己的想法/方法

我想在內存(數據緩沖區)中使用類似文件的對象。 我沒有在Python中使用此概念的經驗,因此我進行了一些研究,並且我了解[緩沖區]就像一個文件,該文件在程序執行時保留在內存中,並且具有與標准系統非常相似的屬性文件。 我認為使用它可能是一個好主意,但是我無法找到解決這些不便的方法:

  1. 由於它仍然文件(類似文件的對象)一樣,如果兩個進程在對象上的操作重合,會不會引發版本不兼容錯誤/錯誤呢? 我只需要向一個進程附加數據(在末尾),並從另一個進程的開頭移除數據(作為某種隊列)。 這種Python功能是否允許這樣做?如果可以,我可以在文檔中確切地查找哪些方法?

  2. 對於上面的解釋,我考慮過使用字面上的隊列。 但是,這可能在時間上執行效率不高(根據我在自己的機器上進行的測試(最適合哪種對象類型),追加到列表的速度相當快,但是追加到熊貓對象的速度要慢1000倍左右)。 是否有一個對象(如果不是類似於文件的對象)可以讓我做到這一點並且效率很高? 我知道效率是主觀的,因此可以說每秒100次附加,沒有明顯的延遲(在這種情況下,時間戳很重要)。

  3. 由於我使用的是兩個不同的進程,並且它們在Python中不共享內存,因此在處理類似文件的對象時是否仍然可以指向相同的內存地址? 如我所說,我通過套接字與它們通信,但是該方法是afaik按值調用,而不是引用。 所以這對我來說似乎是一個嚴重的問題(也許有必要將它們合並為兩個線程,而不是不同的python進程嗎?)

如果您需要其他任何細節,請發表評論,我將很樂意回答。

編輯:在評論中提出的問題:

您如何創建這些流程? 通過像multiprocessingsubprocess這樣的Python模塊,還是其他方式?

我將它們作為兩個完全獨立的程序運行。 每個都有一個不同的主Python文件,該文件由Shell腳本調用; 但是,如果需要,我可以靈活地更改此行為。

另一方面,從傳感器讀取數據的過程有兩個線程:一個從字面上讀取數據,另一個則偵聽套接字請求。

您從傳感器獲取並發送到服務器的數據類型是什么?

通常,我要發送包含浮點數的表,但是傳感器也可能會產生視頻流或其他類型的數據結構。

對隊列的誤解 大熊貓

我知道隊列與數據幀無關。 我只是說我試圖使用一個數據框,但它表現不佳,因為它被認為是預先分配了所需的內存空間(如果我是對的)。 我只是對解決方案的性能表示關注。

首先,您實際上正在考慮完全構建io.BytesIO已經完成的工作。 這是一個類似文件的對象,完全存儲在內存中。 每個進程的對象都完全獨立於其他每個進程的對象。 這就是您想要的一切。 但這對您沒有好處。 它是一個類似文件的對象,這並不意味着可以從其他進程訪問它。 事實上,這不屬於文件類文件對象的整 :他們不是文件。

但是您可以明確地鎖定文件。

的確,除了Windows之外,大多數操作系統不會自動鎖定文件,有些甚至沒有“強制性”鎖,只有協作鎖才真正保護文件,除非所有程序都編寫為使用鎖。 但這不是問題。

一種選擇是為Windows和Unix編寫單獨的代碼:在Windows上,依靠以獨占模式打開文件;在Windows上,以獨占模式打開文件。 在Unix上,使用flock

另一個選項是創建手動鎖定文件。 您可以通過僅使用帶有O_CREAT|O_EXCL標志的os.open原子地嘗試創建文件,如果有人首先在每個平台上首先使該文件失敗,則可以在此基礎上構建其他所有所需的文件。


如果您正在考慮使用共享內存,除非您正在使用multiprocessing ,否則以跨平台的方式進行操作會很痛苦。

但是,通過使用常規文件並在每個進程中使用mmap來訪問文件,就好像文件是普通內存一樣,您可以獲得相同的效果。 只要確保僅將跨平台值用於lengthaccess (不要使用protflags等平台特定的參數),並且它在任何地方都以相同的方式工作。

當然,您不能將Python對象放入共享內存或mmap中,但可以將原始字節或“本機值”或它們的數組或ctypes它們的Structures ,或者最好是多維numpy數組放入其中。 對於除最后一個以外的所有對象,即使不使用模塊,也可以使用適當的包裝對象進行multiprocessing 對於最后一個,只需使用np.memmap而不是直接使用mmap ,它可以處理所有事情。


但是,您可能認為隊列速度更快是正確的。 如果這確實是一個問題(盡管我實際上已經構建並測試了它,然后在解決之前查看它是否是問題...),則繼續進行。 但是您似乎在那里有些誤解。

首先,我不知道您為什么認為隊列與附加到pandas DataFrames有關。 我想您可以將df用作隊列,但是兩者之間沒有內在聯系。

同時,列表適合較小的隊列,但對於較大的隊列則不是。 您可以追加到右側並從左側彈出,也可以追加到左側並從右側彈出。 無論哪種方式,左邊的操作都花費時間與隊列大小成線性關系,因為您必須將整個列表be左右移動一個插槽。 解決方案是collections.deque ,它是一個與列表幾乎相同的對象,不同之處在於它可以在兩側(而不是僅在右側)在恆定時間內插入或刪除。

但是同樣,這並不能解決任何問題,因為它實際上並未以任何方式共享。 您需要某種類型的進程間隊列,而DataFrame或列表(也不是雙端隊列)都無濟於事。

您可以在管道頂部構建進程間隊列。 根據進程的運行方式,這可能是匿名管道,啟動程序將管道的末端移交給子程序,也可能是命名管道,這在Windows與Unix上略有不同,但是在在這兩種情況下,這兩個程序都可以使用具有某些全局已知名稱(例如文件系統路徑)的文件來打開同一管道。

您還可以在TCP套接字的頂部構建進程間隊列。 如果綁定到localhost並連接到localhost,這幾乎與管道一樣高效,但是編寫跨平台更簡單。

那么,如何在管道或套接字的頂部建立隊列? 唯一的問題是您只有字節流而不是消息流。

  • 如果消息的大小都相同,則只需在一側sendall ,然后循環recv ,直到sendall MESSAGESIZE個字節。
  • 如果它們采用諸如pickle之類的自定界格式,那就沒有問題; 只是sendall在一邊,和recv ,直到你對對方一個完整的咸菜。 您甚至可以使用socket.makefile (當然,僅用於套接字,而不是管道)來獲取類似文件的對象,您可以直接將pickle.dump' and傳遞給pickle.dump' and pickle.load`。
  • 您可以使用某種定界符(例如,如果您的消息是永遠不包含換行符或永遠不包含NUL字節的文本,則可以僅使用換行符或0作為定界符-如果您使用換行符,則makefile會注意再次給您)。
  • 或者,您可以在消息本身之前發送每個消息的大小(例如,使用諸如netstring這樣的簡單協議)。

如果您正在(或可能)使用multiprocessing庫來控制所有單獨的進程,則它帶有內置的Queue類,該類在對每個主要平台有效地通過管道發送泡菜的基礎上構建IPC隊列。 ,但您不必擔心它的工作方式; 您只需將隊列交給您的子進程,就可以put另一端, get繼續工作就可以了。

您誤解了什么是文件狀對象。 “文件狀對象”描述了對象所呈現的接口-諸如readwrite類的方法以及逐行迭代。 它沒有說明是否將數據存儲在內存中。 常規文件對象是類似文件的對象。 OS級管道的文件對象是類似文件的對象。 io.StringIOio.BytesIO對象是類似於文件的對象,它們實際上確實像您在想的那樣工作。

與其考慮類文件對象,不如考慮使用哪種OS級機制在進程之間進行通信。 您已經有了套接字; 為什么不使用套接字在進程之間發送數據? 管道將是另一種選擇。 共享內存是可能的,但是依賴於平台且棘手。 這可能不是最佳選擇。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM