114、Python并发编程:一文搞懂为什么以及如何使用共享内存

南宫理的日志录 2024-12-09 08:23:10
引言

前面我们已经介绍了进程间通信(IPC)机制中的消息队列和管道,已经能够实现常见的IPC的场景需求,似乎已经够用了。那么,还有没有必要了解共享内存,本文就来尝试回答关于共享内存的为什么和如何用的问题。

本文的主要内容有:

1、消息队列和管道的内部实现及局限性

2、什么是共享内存

3、共享内存的使用示例

4、共享内存的注意事项

消息队列和管道的内部实现及局限性

队列(multiprocessing.Queue)

前面已经介绍过,通过Queue实现进程间通信的方式。我们已经了解到Queue具有FIFO、阻塞的特性,非常适合生产者和消费者模式中的多个进程之间的任务分配和数据传输。

multiprocessing.Queue实际上是基于multiprocessing.Pipe和共享内存来实现的。数据是通过管道在进程间进行传输的,需要使用序列化(通常是pickle,从一些报错中可以看到pickle的存在)来发送对象数据。

管道(multiprocessing.Pipe)

基于管道的进程间通信,相对于队列更加高效,更加适合低延迟的双向通信(也可以是单向的),但是,只适用于两个进程之间的通信,不如队列灵活。

multiprocessing.Pipe是基于底层的操作系统管道实现的,使用文件描述符进行数据传输,数据也是需要通过序列化的方式进行发送。

局限性

从上面的描述中也可以看出,在使用管道、消息队列进行进程间通信时,主要会存在以下局限性:

1、两者都是基于消息传递的机制来实现的,数据需要进行序列化和反序列的操作。

2、队列适合生产者-消费者模式下的进程通信,管道只适用于一对一的进程通信。

3、由于管道和队列涉及到序列化和通信的开销,性能比较低,虽然使用起来简单,但是只适合小量数据的传送。

需要特别说明的是,这些局限性只是在特定需求场景下才会成为问题,如果在您的实际需求场景中,队列或者管道已经够用,还是应该优先选择队列或管道,毕竟编程实现简单,而且代码更加易于阅读和维护。

什么是共享内存

由于队列和管道存在相应的性能开销,所以在大量数据传输的场景中,可能就会显得有些乏力,所以,就需要考虑使用更加底层的共享内存的方式来实现进程间通信。

所谓共享内存,是一种进程间通信(IPC)的机制,它允许多个进程访问同一块物理内存区域。通过共享内存,进程能够在读取和写入数据时避免数据复制,从而提高性能,尤其是在需要频繁交换数据的情况下。

共享内存的基本操作包括:创建共享内存、写入数据和读取数据。

在Python中,可以使用multiprocessing.shared_memory模块来创建和使用共享内存。

共享内存的使用示例

接下来,通过Python代码来简单演示一下共享内存模块的使用。

直接看代码:

运行结果:

基于以上代码简单说明一下共享内存的使用:

1、使用shared_memory.SharedMemory创建一个共享内存区域,需要指定大小,会自动生成共享内存的name属性。

结合SharedMemory对象的初始化方法,可以看出,创建一个新的共享内存区域时,需要指定create为True,并同时指定size大小。在子进程链接一个已经存在的共享内存时,则只需要指定name属性即可。

2、共享内存区域是无结构的bytes类型,需要将共享内存关联到对应的数据结构上,比如Numpy数组,这样通过使用Numpy数组的方式对共享内存使用,更加方便。

3、在子进程中通过共享内存的name属性,链接上已经存在的共享内存。

4、在子进程中同样需要通过数据结构映射到共享内存,从而方便地对共享内存进行使用。

5、共享内存的链接在使用完成后,需要调用close()方法关闭。

6、当没有进程再使用共享内存时,需要调用unlink()方法,以确保共享内存的资源被正确释放。

共享内存的注意事项

相较于队列、管道等IPC机制,共享内存是一种更加接近底层、更加高效的IPC机制,特别适合大量数据的传输。如果涉及到有性能要求的进程间通信的场景,可以考虑使用共享内存。

但是,为了能够更好地使用共享内存,还需要注意以下事项:

1、数据一致性问题

共享内存并不自动提供同步机制,因此需要通过其他方法,比如multiprocessing.Lock等,来确保数据的一致性,尤其是在多个进程同时读写共享内存的场景中。

2、主动内存管理

在使用共享内存时,需要确保在使用完成后,显式调用close()方法和unlink()方法,从而确保内存资源被正确释放。unlink()方法调用后,会删除共享内存的标识符,使其不可用。

如果没有显式调用相应的方法,可能会导致内存泄露的问题发生。

3、数据结构

由于共享内存更偏向于底层的存储,内部更多的是字节存储,在实际使用中,需要选择恰当的数据结构,对共享内存进行映射,从而更急灵活地使用共享内存。

4、性能考虑

尽管共享内存比其他IPC机制性能更好,但是,如果没有进行正确的使用,可能会导致复杂性和性能的问题。此外,在使用中,需要仔细考虑数据的读写模式和共享内存区域的空间大小。

5、跨平台兼容性

Python的共享内存在不同操作系统上的实现可能会略有不同,所以在移植到其他平台时,需要进行充分的测试,以保证程序能够正常运行。

总结

本文以队列和管道在IPC中的性能等方面的局限性,引入了共享内存这种IPC机制。分别介绍了共享内存的概念,共享内存的使用,以及共享内存的注意事项。共享内存在提高性能的同时,也带来了使用上的复杂性。所以,在实际业务场景中,需要根据具体需求进行权衡取舍,从而选用更加合适的IPC机制。

以上就是本文的全部内容,感谢您的拨冗阅读,希望对您有所帮助!

0 阅读:3

南宫理的日志录

简介:深耕IT科技,探索技术与人文的交集