为数据缓冲区创建类似文件的对象

时间:2018-08-22 17:01:01

标签: python python-3.x buffer

情境化

我正在编写一个程序,该程序能够从传感器读取数据,然后对其进行处理。目前,我希望将其发送到服务器。我有两个通过套接字进行通信的进程,一个进程读取数据并将其存储到一个临时文件,另一个进程读取该临时文件并将数据发送到服务器。

问题

问题实际上从来没有在测试中出现过,但是我已经意识到,如果采样频率很高,那么两个进程很有可能同时尝试读取/写入文件< / strong>(不是他们完全同时请求了它们,而是一个在另一个关闭之前尝试将其打开)。

即使这不会引发错误(对于我在网上阅读的内容,某些操作系统也不会在文件中添加锁),它也可能导致巨大的版本不兼容错误,从而导致数据丢失。因此,这种处理数据的方式看起来不太合适。

我自己的想法/方法

我想在内存(数据缓冲区)中使用类似文件的对象。我没有在Python中使用此概念的经验,因此我进行了一些研究,并且我了解[缓冲区]就像一个文件,该文件在程序执行时保留在内存中,并且具有与标准系统非常相似的属性文件。我认为使用它可能是一个好主意,但是我找不到解决这些不便的方法:

  1. 因为它仍然像文件(em> )(文件样的对象),所以如果两个进程在对象上的表现相吻合,则可能不是版本不兼容错误/错误可以提高吗?我只需要在一个进程中追加数据(在末尾),并从另一个进程开始删除数据(作为某种队列)。该Python功能是否允许这样做?如果可以,我可以在文档中确切地查找哪些方法?

  2. 对于上面的解释,我考虑过使用字面上的队列;但是,这可能在时间上执行效率不高(根据我在自己的机器上进行的测试(最适合哪种对象类型),追加到列表的速度相当快,但是追加到熊猫对象的速度要慢1000倍左右)。是否有一个对象(如果不是类似于文件的对象)可以让我做到这一点并且效率很高?我知道效率是主观的,因此可以说每秒100次附加,没有明显的延迟(在这种情况下,时间戳很重要)。

  3. 由于我使用的是两个不同的进程,而这些进程在Python中不共享内存,因此在操作类似文件的对象时是否仍然可以指向相同的内存地址?如我所说,我通过套接字与它们通信,但是该方法是afaik按值调用,而不是引用。所以这对我来说似乎是一个严重的问题(也许有必要将它们合并为两个线程,而不是不同的python进程吗?)

如果您需要其他任何细节,请发表评论,我将很乐意回答。

编辑:评论中提出的问题:

  

您如何创建这些流程?通过像这样的Python模块   multiprocessingsubprocess或其他方式?

我将它们作为两个完全独立的程序运行。每个都有一个不同的主Python文件,该文件由Shell脚本调用;但是,如果需要,我可以灵活地更改此行为。

另一方面,从传感器读取数据的过程有两个线程:一个从字面上读取数据,另一个从侦听套接字请求。

  

您要从传感器获取什么类型的数据并将其发送到   服务器?

我通常要发送包含浮点数的表,但是传感器也可能会产生视频流或其他类型的数据结构。

  

对队列的误解|熊猫

我知道队列与数据帧无关;我只是说我试图使用一个数据框,但它表现不佳,因为它被认为是预先分配了所需的内存空间(如果我是对的)。我只是对解决方案的性能表示关注。

2 个答案:

答案 0 :(得分:3)

首先,您确实正在考虑完全构建io.BytesIO已经完成的工作。这是一个类似文件的对象,完全存储在内存中。每个进程的对象完全独立于其他每个进程的对象。这就是您想要的一切。但这对您没有好处。它是一个类似文件的对象,并不意味着可以从其他进程中访问它。实际上,这就是不是文件的文件状对象的全部 point

但是您可以明确地锁定文件。

的确,除了Windows之外,大多数操作系统不会自动锁定文件,有些甚至没有“强制性”锁,只有协作锁才能真正保护文件,除非编写了所有程序。使用锁。但这不是问题。

一种选择是为Windows和Unix编写单独的代码:在Windows上,依赖于以独占模式打开文件;在Unix上,请使用flock

另一个选择是创建手动锁定文件。您可以原子地尝试创建一个文件,如果有人首先在每个平台上首先通过将os.openO_CREAT|O_EXCL标志一起使用来使它失败,则可以在此之上构建您需要的其他所有文件。 / p>


如果您正在考虑使用共享内存,除非您使用multiprocessing,否则以跨平台的方式进行操作会很痛苦。

但是,通过使用常规文件并在每个进程中使用mmap来访问文件,就可以得到相同的效果,就好像文件是普通内存一样。只需确保仅对lengthaccess使用跨平台值(而不使用平台特定的参数,例如protflags),它的工作原理是相同的到处都是

当然,您不能将Python对象放入共享内存或mmap中,但可以将原始字节或“本机值”,它们的数组或它们的ctypes Structures放入其中,或者,最重要的是,它们的多维numpy数组。对于除最后一个以外的所有模块,即使没有使用该模块,也可以使用the appropriate wrapper objects out of multiprocessing。对于最后一个,只需使用np.memmap而不是直接使用mmap,它就可以处理一切。


但是,您可能对队列更快感到正确。如果这确实是一个问题(尽管我实际上已经构建并对其进行了测试,以在解决之前查看它是否有问题...),则继续进行。但是您似乎那里有些误解。

首先,我不知道您为什么认为队列与附加到pandas DataFrames有关。我想您可以使用df作为队列,但是两者之间没有内在联系。

同时,列表适合较小的队列,但对于较大的队列则不行。您可以追加到右侧并从左侧弹出,也可以追加到左侧并从右侧弹出。无论哪种方式,左边的操作都花费时间与队列大小成线性关系,因为您必须将整个列表be左右移动一个插槽。解决这个问题的方法是collections.deque,该对象与列表几乎相同,不同之处在于它不仅可以在右侧而且可以在两侧恒定的时间插入或删除。

但是,这仍然不能解决任何问题,因为它实际上并未以任何方式共享。您需要某种 interprocess 队列,而DataFrame或列表(也不是双端队列)都没有帮助。

您可以在管道顶部构建进程间队列。根据进程的运行方式,这可能是匿名管道,启动程序将管道的末端移交给子程序,也可能是命名管道,这在Windows与Unix上略有不同,但是在在这两种情况下,这两个程序都可以使用具有某些全局已知名称(例如文件系统路径)的文件来打开同一管道。

您还可以在TCP套接字的顶部构建进程间队列。如果您绑定到本地主机并连接到本地主机,这几乎与管道一样高效,但是编写跨平台会更简单。

那么,如何在管道或套接字的顶部建立队列?唯一的问题是您只有字节流而不是消息流。

  • 如果您的邮件大小相同,则只需sendall一侧,然后recv循环,直到拥有MESSAGESIZE个字节。
  • 如果它们采用pickle之类的自定格式,就没有问题;一侧sendall,另一侧recv,直到腌制完为止。您甚至可以使用socket.makefile(当然,仅适用于套接字,而不是管道)来获取类似文件的对象,您可以直接将其传递给pickle.dump‘ and pickle.load`。
  • 您可以使用某种分隔符(例如,如果您的消息是永远不包含换行符或永远不包含NUL字节的文本,则可以仅使用换行符或0作为分隔符,如果您使用换行符, makefile再次为您解决这个问题。
  • 或者您可以在消息本身之前发送每条消息的大小(例如,使用诸如netstring这样的简单协议)。

如果您正在(或可能)使用multiprocessing库来控制所有单独的进程,它将内置一个Queue类,该类在发送泡菜的顶部构建IPC队列以高效的方式遍历每个主要平台,但您不必担心它的工作方式;您只需将队列交给您的子进程,就可以在一端put,而在另一端get进行操作。

答案 1 :(得分:2)

您误解了什么是文件状对象。 “类文件对象”描述了对象所呈现的接口-readwrite之类的方法,以及逐行迭代。它没有说明是否将数据存储在内存中。常规文件对象是类似文件的对象。 OS级管道的文件对象是类似文件的对象。 io.StringIOio.BytesIO对象是类似于文件的对象,它们实际上确实像您在想的那样工作。

您可能无需考虑要使用类似文件的对象的思想,而是要考虑使用哪种OS级机制在进程之间进行通信。您已经有了套接字;为什么不使用套接字在进程之间发送数据?管道将是另一种选择。共享内存是可能的,但是依赖于平台且棘手。这可能不是最佳选择。