繁体   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