可以在两个独立的进程之间共享内存数据吗?

时间:2009-08-12 19:33:11

标签: python

我有一个使用Twisted的xmlrpc服务器。服务器有大量的数据存储在内存中。是否可以运行一个可以访问第一台服务器内存中对象的辅助单独xmlrpc服务器?

因此,serverA启动并创建一个对象。 serverB启动并可以从serverA中的对象读取。

*编辑*

要共享的数据是一个包含100万个元组的列表。

10 个答案:

答案 0 :(得分:106)

如果没有深入和暗淡地重写Python核心运行时(为了允许强制使用给定的共享内存段并确保不同进程之间的兼容地址的分配器),就无法在任何内容中“共享内存中的对象”一般意义。该列表将包含一百万个元组地址,每个元组由其所有项目的地址组成,这些地址中的每一个都将由pymalloc以一种不可避免地在进程之间变化并在整个堆中传播的方式分配。

对于除Windows之外的几乎所有系统,只要父进程不改变这些对象,就可以生成对父进程空间中的对象具有基本只读访问权限的子进程。这是通过调用os.fork()获得的,实际上“快照”了当前进程的所有内存空间,并在复制/快照上启动另一个同时进程。在所有现代操作系统上,由于“写入时复制”方法,这实际上非常快:虚拟内存的页面在fork之后没有被任何进程更改,而是没有真正复制(相同页面的访问是共享的) ;只要任一进程修改先前共享页面中的任何位,poof,该页面被复制,页面表被修改,因此修改过程现在有自己的副本,而另一个进程仍然看到原始页面。

这种非常有限的共享形式在某些情况下仍然可以成为救星(尽管它非常有限:请记住,例如,由于引用计数,添加对共享对象的引用会被视为“改变”该对象,因此强制页面复制!)...除了在Windows上,当然,它不可用。有了这个例外(我认为不会涵盖你的用例),共享包含其他对象的引用/指针的对象图基本上是不可行的 - 几乎任何对现代语言感兴趣的对象(包括Python)属于这种分类。

在极端(但足够简单)的情况下,可以通过放弃这种对象图的本机存储器表示来获得共享。例如,一个包含十六个浮点数的一百万个元组的列表实际上可以表示为128 MB共享内存的单个块 - 所有16M浮点数都是双精度IEEE表示的端到端放置 - 有一些小垫片顶部“使它看起来像”你正在以正常的方式解决问题(当然,不那么小的一点一点的垫片也必须处理极其毛茸茸的进程间同步问题,肯定会出现;-)。它从那里变得更加毛茸茸,更加复杂。

现代并发方法越来越蔑视共享 - 任何方法都支持无共享方式,其中任务通过消息传递进行通信(即使在使用线程和共享地址空间的多核系统中,同步问题和性能当大量内存被多个内核主动修改时,就会导致硬件在缓存,流水线停滞等方面受到影响,正在推动人们离开)。

例如,Python标准库中的多处理模块主要依赖于酸洗和来回发送对象,而不是共享内存(当然不是以R / W方式! - )。

我意识到这对OP来说不是一个受欢迎的新闻,但是如果他确实需要将多个处理器用于工作,那么他最好考虑一下他们必须共享的任何内容驻留在可以访问和修改它们的地方。消息传递 - 一个数据库,一个内存缓存集群,一个专门的进程,除了将这些数据保存在内存中并根据请求发送和接收它们之外什么都不做,以及其他这样的以消息传递为中心的体系结构。

答案 1 :(得分:16)

mmap.mmap(0, 65536, 'GlobalSharedMemory')

我认为对于希望共享相同内存的所有进程,标记(“GlobalSharedMemory”)必须相同。

http://docs.python.org/library/mmap.html

答案 2 :(得分:10)

在Python中有一些 1 第三方库可用于低级共享内存操作:

  • sysv_ipc
    • >对于posix不合规系统
  • posix_ipc
    • >在Windows中使用cygwin

两者都可用via pip

[1]另一个软件包shm可用,但已弃用。有关库的比较,请参阅this page

Example Code用于C到Python通信c/o Martin O'Hanlon

shmwriter.c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>

int main(int argc, const char **argv)
{
   int shmid;
   // give your shared memory an id, anything will do
   key_t key = 123456;
   char *shared_memory;

   // Setup shared memory, 11 is the size
   if ((shmid = shmget(key, 11, IPC_CREAT | 0666)) < 0)
   {
      printf("Error getting shared memory id");
      exit(1);
   }
   // Attached shared memory
   if ((shared_memory = shmat(shmid, NULL, 0)) == (char *) -1)
   {
      printf("Error attaching shared memory id");
      exit(1);
   }
   // copy "hello world" to shared memory
   memcpy(shared_memory, "Hello World", sizeof("Hello World"));
   // sleep so there is enough time to run the reader!
   sleep(10);
   // Detach and remove shared memory
   shmdt(shmid);
   shmctl(shmid, IPC_RMID, NULL);
}

shmreader.py

import sysv_ipc

# Create shared memory object
memory = sysv_ipc.SharedMemory(123456)

# Read value from shared memory
memory_value = memory.read()

# Find the 'end' of the string and strip
i = memory_value.find('\0')
if i != -1:
    memory_value = memory_value[:i]

print memory_value

答案 3 :(得分:4)

您可以编写一个C库来为特定目的创建和操作共享内存数组,然后使用ctypes从Python访问它们。

或者,将它们放在/ dev / shm(即tmpfs)中的文件系统上。您可以节省大量的开发工作,只需很少的性能开销:来自tmpfs文件系统的读/写只是一个memcpy。

答案 4 :(得分:2)

答案 5 :(得分:2)

实际上很简单。你可以只使用共享内存。此示例在C ++中创建元组(python)列表,并与python进程共享,然后可以使用元组列表。要在两个Python流程之间使用,只需在发件人流程上以ACCESS_WRITE进行访问,然后调用write方法。

C ++(发件人流程):

#include <windows.h>
#include <stdio.h>
#include <conio.h>
#include <tchar.h>
#include <iostream>
#include <string>

#define BUF_SIZE 256
TCHAR szName[]=TEXT("Global\\MyFileMappingObject");
TCHAR szMsg[]=TEXT("[(1, 2, 3), ('a', 'b', 'c', 'd', 'e'), (True, False), 'qwerty']");

int _tmain(int argc, _TCHAR* argv[])
{
     HANDLE hMapFile;
   LPCTSTR pBuf;

   hMapFile = CreateFileMapping(
                 INVALID_HANDLE_VALUE,    // use paging file
                 NULL,                    // default security
                 PAGE_READWRITE,          // read/write access
                 0,                       // maximum object size (high-order DWORD)
                 BUF_SIZE,                // maximum object size (low-order DWORD)
                 szName);                 // name of mapping object

   if (hMapFile == NULL)
   {
      _tprintf(TEXT("Could not create file mapping object (%d).\n"),
             GetLastError());
      return 1;
   }
   pBuf = (LPTSTR) MapViewOfFile(hMapFile,   // handle to map object
                        FILE_MAP_ALL_ACCESS, // read/write permission
                        0,
                        0,
                        BUF_SIZE);

   if (pBuf == NULL)
   {
      _tprintf(TEXT("Could not map view of file (%d).\n"),
             GetLastError());

       CloseHandle(hMapFile);
       return 1;
   }

   CopyMemory((PVOID)pBuf, szMsg, (_tcslen(szMsg) * sizeof(TCHAR)));
    _getch();

   UnmapViewOfFile(pBuf);

   CloseHandle(hMapFile);
    return 0;
}

Python(接收者进程):

import mmap
shmem = mmap.mmap(0,256,"Global\\MyFileMappingObject",mmap.ACCESS_READ)
msg_bytes = shmem.read()
msg_utf16 = msg_bytes.decode("utf-16")
code = msg_utf16.rstrip('\0')
yourTuple = eval(code)

答案 6 :(得分:2)

您可以在3.8中使用 collectionReference.whereEqualTo("barCode", scanResult).get().addOnCompleteListener(new OnCompleteListener<QuerySnapshot>() { @Override public void onComplete(@NonNull Task<QuerySnapshot> task) { if (task.isSuccessful()) { for (QueryDocumentSnapshot queryDocumentSnapshot : task.getResult()) { if (queryDocumentSnapshot.getString("barCode") != null) { // Success intent to ViewData collectionReference.document(queryDocumentSnapshot.getId()).update("productQuantity", FieldValue.increment(1)); Intent moveView = new Intent(ScannersActivity.this, ViewData.class); moveView.putExtra("documentID", documentID); startActivity(moveView); finish(); } else { // Problem here.. Intent moveCode = new Intent(ScannersActivity.this, AddItems.class); moveCode.putExtra("sendDocumentID", documentID); moveCode.putExtra("ScanResult", scanResult); startActivity(moveCode); finish(); } } } } });

https://docs.python.org/3.8/library/multiprocessing.shared_memory.html#module-multiprocessing.shared_memory

答案 7 :(得分:1)

为什么不将共享数据粘贴到memcache服务器中?然后两台服务器都可以轻松访问它。

答案 8 :(得分:0)

如果您的数据只是元组,并且您愿意将其作为

进行访问
  • (nrows x tuplewidth)np.ndarray s或
  • n 1d np.ndarrays

然后我强烈推荐使用numpy的memmap包装器。

我的理解是:

  • 将numpy数组保存为包含原始数组内容的平面memmap文件
  • 每个进程将一个ndarray指向memmap文件作为其后备数据。文档链接显示了如何。

这适用于只读数据。 如果您想要读写,则需要使用多进程锁来保护访问。

因为memmap使用分页来加载数据,因此从磁盘访问大型数据集的速度非常快。事实上,我不认为现代操作系统从磁盘加载数据到内存的速度要快于此 - 不涉及序列化。

答案 9 :(得分:-1)

为什么不将数据库用于共享数据?您有许多轻量级选项,您无需担心并发问题:sqlite,任何nosql / key-value数据库品种等。

相关问题