• python多进程之间共享内存


    一、为什么要用到共享内存

    进程之间交换数据我们可以通过建立本地RPC,但往往比较慢,因为要花时间去执行数据传递。
    此时,如果有一个实时性要求比较高的跨进程功能,共享内存就是一个不错的选择。

    1、什么是共享内存?

    1、共享内存是一种在相同机器中两个正在运行的进程之间共享和传递数据的有效方式,不同进程之间共享的内存通常安排为同一段物理内存;顾名思义,共享内存就是允许两个不相关的进程访问同一个逻辑内存,不同进程可以同时对这段内存执行写、读的操作,这意味着高效的数据同步

    2、共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。因此如果要确保数据的准确性,我们需要采用其他机制来确保,这意味着数据不安全

    二、什么是mmap

    mmap可以将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。不同进程就可以采用指针的方式读取这一段内存。

    三、mmap在python中的应用

    python提供了mmap库,效果同上面的mmap。

    1、mmap中有哪些方法可以使

    m.close()   关闭 m 对应的文件;

    m.find(str, start=0)   从 start 下标开始,在 m 中从左往右寻找子串 str 最早出现的下标;

    m.flush([offset, n])   把 m 中从offset开始的n个字节刷到对应的文件中,参数 offset 要么同时指定,要么同时不指定;

    m.move(dstoff, srcoff, n)   等于 m[dstoff:dstoff+n] = m[srcoff:srcoff+n],把从 srcoff 开始的 n 个字节复制到从 dstoff 开始的n个字节,可能会覆盖重叠的部分。

    m.read(n)   返回一个字符串,从 m 对应的文件中最多读取 n 个字节,将会把 m 对应文件的位置指针向后移动;

    m.read_byte()   返回一个1字节长的字符串,从 m 对应的文件中读1个字节,要是已经到了EOF还调用 read_byte(),则抛出异常 ValueError;

    m.readline()   返回一个字符串,从 m 对应文件的当前位置到下一个’\n’,当调用 readline() 时文件位于 EOF,则返回空字符串;

    m.resize(n)   把 m 的长度改为 n,m 的长度和 m 对应文件的长度是独立的;

    m.seek(pos, how=0)   同 file 对象的 seek 操作,改变 m 对应的文件的当前位置;

    m.size()   返回 m 对应文件的长度(不是 m 对象的长度len(m));

    m.tell()   返回 m 对应文件的当前位置;

    m.write(str)   把 str 写到 m 对应文件的当前位置,如果从 m 对应文件的当前位置到 m 结尾剩余的空间不足len(str),则抛出 ValueError;

    m.write_byte(byte)   把1个字节(对应一个字符)写到 m 对应文件的当前位置,实际上 m.write_byte(ch) 等于 m.write(ch)。如果 m 对应文件的当前位置在 m 的结尾,也就是 m 对应文件的当前位置到 m 结尾剩余的空间不足1个字节,write() 抛出异常ValueError,而 write_byte() 什么都不做。

    2、代码实现

    # -*- coding: utf-8 -*-
    # 进程之间共享内存 
    # client1.py
    import mmap
    
    mmap_file = None
    
    
    # 从内存中读取信息,
    def read_mmap_info():
        global mmap_file
        mmap_file.seek(0)
        # 把二进制转换为字符串
        info_str = mmap_file.read().translate(None, b'\x00').decode()
        return info_str
    
    
    # 如果内存中没有对应信息,则向内存中写信息以供下次调用使用
    def get_mmap_info():
        global mmap_file
        # 第二个参数1024是设定的内存大小,单位:字节。如果内容较多,可以调大一点
        mmap_file = mmap.mmap(0, 1024, access=mmap.ACCESS_WRITE, tagname='share_mmap')
        return read_mmap_info()
    
    
    if __name__ == "__main__":
        result = get_mmap_info()
        print("====", result)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    # -*- coding: utf-8 -*-
    # 进程之间共享内存
    # client2.py
    import mmap
    import os
    
    mmap_file = None
    
    
    # 如果内存中没有对应信息,则向内存中写信息以供下次调用使用
    def set_mmap_info():
        global mmap_file
        mmap_file = mmap.mmap(0, 1024, access=mmap.ACCESS_WRITE, tagname='share_mmap')
        mmap_file.write(b"12580")
    
    
    # 修改内存块中的数据
    def reset_mmp_info():
        global mmap_file
        mmap_file.seek(0)
        mmap_file.write(b'100')
    
    
    if __name__ == "__main__":
        set_mmap_info()
        os.system("pause")
        reset_mmp_info()
        os.system("pause")
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    # -*- coding: utf-8 -*-
    # 进程之间共享内存
    # client3.py
    import mmap
    import os
    
    mmap_file = None
    
    
    ##如果内存中没有对应信息,则向内存中写信息以供下次调用使用
    def set_mmap_info():
        global mmap_file
        mmap_file = mmap.mmap(0, 1024, access=mmap.ACCESS_WRITE, tagname='share_mmap')
        mmap_file.write(b"2222")
    
    
    ##修改内存块中的数据
    def reset_mmp_info():
        global mmap_file
        mmap_file.seek(0)
        mmap_file.write(b'999')
    
    
    if __name__ == "__main__":
        set_mmap_info()
        os.system("pause")
        reset_mmp_info()
        os.system("pause")
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    上述三个进程,client1为读取信息打印信息的,client2、client3为写入信息的
    运行顺序为client2 -> client1 -> client3 ->client1 -> client2 -> client1 -> client3 -> client1
    可以看到在client1下面会分别显示
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    实际上我们输入的是12580 -> 2222 -> 100 -> 999,但得到的是12580 -> 22220 -> 10020 -> 99920 因此我们可以很很清晰的得出对于内存块,不像对象那样会将对象的原数据清除而是直接在对应内存处覆盖数据。那这样实际上得到的并不是我们想要的数据。

    3、如何使数据成为我们想要的数据

    # -*- coding: utf-8 -*-
    # 进程之间共享内存
    # client2.py
    import mmap
    import os
    
    mmap_file = None
    
    
    # 如果内存中没有对应信息,则向内存中写信息以供下次调用使用
    def set_mmap_info():
        global mmap_file
        mmap_file = mmap.mmap(0, 1024, access=mmap.ACCESS_WRITE, tagname='share_mmap')
        mmap_file.write(b"12580")
    
    
    # 修改内存块中的数据
    def reset_mmp_info():
        global mmap_file
        mmap_file.move(0, 10, 10)
        mmap_file.seek(0)
        mmap_file.write(b'100')
    
    
    if __name__ == "__main__":
        set_mmap_info()
        os.system("pause")
        reset_mmp_info()
        os.system("pause")
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    # -*- coding: utf-8 -*-
    # 进程之间共享内存
    # client3.py
    import mmap
    import os
    
    mmap_file = None
    
    
    ##如果内存中没有对应信息,则向内存中写信息以供下次调用使用
    def set_mmap_info():
        global mmap_file
        mmap_file = mmap.mmap(0, 1024, access=mmap.ACCESS_WRITE, tagname='share_mmap')
        mmap_file.move(0, 10, 10)
        mmap_file.write(b"2222")
    
    
    ##修改内存块中的数据
    def reset_mmp_info():
        global mmap_file
        mmap_file.move(0, 10, 10)
        mmap_file.seek(0)
        mmap_file.write(b'999')
    
    
    if __name__ == "__main__":
        set_mmap_info()
        os.system("pause")
        reset_mmp_info()
        os.system("pause")
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    暴力的方法,将原有的一定长度的内存移动到另一个地方:mmap_file.move(0, 10, 10),此时结果就跟我们预期的一样了( 自己去验证吧=3=)

    四、PRC间通信与共享内存使用及效率比较

    1、rpc的使用,直接采用这篇文章上的例子《python实现RPC

    我们将上面例子稍作改动~

    # RpcSever.py 改为如下
    import time
    
    @register_function
    def setData(data, t):
        print(data)
        cTime = time.time()
        print(cTime - t)
        return data
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    # RpcClient.py 改为如下
    import time
    
    print(c.setData("哈哈哈哈哈", time.time()))
    
    • 1
    • 2
    • 3
    • 4

    先运行 RpcSever.py, 后运行RpcClient.py,可以得到如下结果:
    在这里插入图片描述
    短短的一次数据传输就会产生时间损耗,如果传输的数据量、频率再往上提升,时间的损耗更是会成倍的提高。
    由此可见共享内存,在传输效率上有着其不可替代的优越性。

  • 相关阅读:
    Node详细解释[带你快速入门Node](2)
    2022阿里巴巴全球数学竞赛 第4题 虎虎生威(盲盒问题、集卡问题)解决思路
    【进阶C语言】进阶指针--学会所有指针类型
    01-JVM-类加载篇
    数字签名和CA证书
    企业集中监控体系思路及架构
    java版工程管理系统Spring Cloud+Spring Boot+Mybatis实现工程管理系统源码
    [R]第二节 练习一关于数值向量
    《机器学习实战》6.支持向量机(SVM)
    使用JMeter测试.Net5.0,.Net6.0框架下无数据处理的并发情况
  • 原文地址:https://blog.csdn.net/weixin_40301728/article/details/126473965