“Immutable"字节数组或对象锁

本文关键字:数组 对象 字节数 字节 Immutable quot | 更新日期: 2023-09-27 18:06:28

我正在开发一个多线程modbus服务器,我需要管理一个字节块供客户端读取。每个modbus设备都有一个线程来更新它们各自的字节数组部分,所以我需要实现某种形式的锁定。

初始化应用程序时,设备的数量和分配给每个设备的内存字节数将保持不变。我做了一些研究,似乎用ReaderWriterLockSlim对象数组作为锁来锁定多维数组是安全的:

    private static ReaderWriterLockSlim[] memLock;
    private static byte[][] memoryBlock;
    ...
    //Modify or read memoryBlock corresponding to deviceIndex as needed using:
    memLock[deviceIndex].EnterWriteLock();
        try
        {
            // Write to array of bytes from memoryBlock
            memoryBlock[2][3] = 0x05;
        }
        finally
        {
            memLock[deviceIndex].ExitWriteLock();
        }
        memLock[deviceIndex].EnterReadLock();
        try
        {
            // Read array of bytes from memoryBlock
        }
        finally
        {
            memLock[deviceIndex].ExitReadLock();
        }

我没有写过很多多线程应用程序,直到最近我才"发现"了不变性的概念。下面是我尝试把上面的变成一个不可变的类,假设一个设备列表和内存大小在内存是Initialize ed后永远不会改变。类是static的原因是因为在我的应用程序中只有一个该类的实例:

    private static class DeviceMemory
    {
        private static bool initialized_ = false;
        private static int memSizeInBytes_;
        private static List<byte[]> registers_;
        public static void Initialize(int deviceCount, int memSizeInBytes)
        {
            if (initialized_) throw new Exception("DeviceMemory already initialized");
            if (memSizeInBytes <= 0) throw new Exception("Invalid memory size in bytes");
            memSizeInBytes_ = memSizeInBytes;
            registers_ = new List<byte[]>();
            for(int i=0; i<deviceCount;++i)
            {
                byte[] scannerRegs = new byte[memSizeInBytes];
                registers_.Add(scannerRegs);
            }
            initialized_ = true;
        }
        public static byte[] GetBytes(int deviceIndex)
        {
            if (initialized_) return registers_[deviceIndex];
            else return null;
        }
        public static void UpdateBytes(int deviceIndex, byte[] memRegisters)
        {
            if (!initialized_) throw new Exception("Memory has not been initialized");
            if (memRegisters.Length != memSizeInBytes_)
                throw new Exception("Memory register size does not match the defined memory size in bytes: " + memSizeInBytes_);
            registers_[deviceIndex] = memRegisters;
        }
    }

以下是我对上述问题的回答:

  1. 我是否正确,我可以锁定一行的二维数组如上所示?
  2. 我是否正确地实现了不可变类?即,您是否看到DeviceMemory类的任何问题,这些问题会阻止我从设备线程写入UpdateBytes方法并同时从不同线程上的多个客户端读取?
  3. 这个不可变类是一个明智的选择比更传统的多维字节数组/锁?具体来说,我担心内存使用/垃圾收集,因为字节数组的更新实际上是"新"字节数组,取代了对旧数组的引用。但是,对旧数组的引用应该在客户端读取它之后立即释放。每秒将有大约35个设备更新,每个设备4个客户端以大约1秒的间隔读取数据。
  4. 哪个解决方案或其他解决方案性能更好,特别是如果服务器要向外扩展?

感谢您的阅读!

“Immutable"字节数组或对象锁

首先,您在第二个代码示例中所展示的不是一个"不可变类",而是一个"静态类"。两个完全不同的东西;你需要复习你的术语,以确保你有效地沟通,以及在研究与之相关的技术时不会感到困惑。

关于你的问题:

1。我是否可以像上面所示的那样锁定二维数组的一行?

您应该使用ReaderWriterLockSlim,并且您没有展示任何机制来捕获超时异常。但是,您可以为byte[][]对象的每个byte[]元素使用单独的锁。

更一般地说,您可以使用锁来表示您想要的任何数据单元或其他资源。锁对象不关心。

2。我是否正确地实现了不可变类?即,您是否看到devicemmemory类的任何问题,这会阻止我从设备线程写入UpdateBytes方法,并同时从不同线程上的多个客户端读取?

如果你的意思是"不可变",那么不是。如果你真的指的是"静态",那么是的,但我不清楚知道这一点是否有用。这个类不是线程安全的,这似乎是你更关心的,所以在这方面你做得不对。

UpdateBytes()方法本身而言,它本身并没有什么问题。事实上,由于在。net中将一个引用从一个变量复制到另一个变量是一个原子操作,因此UpdateBytes()方法可以在其他线程试图检索数组元素的同时"安全地"更新数组元素。"安全"是指阅读器不会得到损坏的数据。

但是在你的类中没有任何东西可以确保Initialize()只被调用一次。此外,如果没有同步(锁或标记变量volatile),就不能保证在一个线程中写入的值将被另一个线程观察到。这包括所有的字段,以及单独的byte[]数组元素。

3。与传统的多维字节数组/锁相比,这个不可变类是明智的选择吗?具体来说,我担心内存使用/垃圾收集,因为字节数组的更新实际上是"新"字节数组,取代了对旧数组的引用。但是,对旧数组的引用应该在客户端读取它之后立即释放。每秒将有大约35个设备更新,每个设备4个客户端以大约1秒的间隔读取数据。

你的问题中没有足够的上下文来进行比较,因为我们不知道你如何访问memoryBlock数组。如果只是将新数组复制到数组中,则两者应该相似。即使只是在第二个示例中创建新数组,那么假设数组不大,我希望每秒生成~100个新对象的速度完全在垃圾收集器的带宽之内。

至于第二个代码示例中的方法是否可取,我将以所有应有的尊重建议您可能应该坚持使用传统的同步代码(即使用ReaderWriterLockSlim,甚至只是普通的lock语句)。使用常规锁来实现并发性已经够困难的了,更不用说尝试编写无锁代码了。考虑到您所描述的更新速率,我希望普通的lock语句可以很好地工作。

如果你遇到一些带宽问题,那么至少你有一个已知的好的实现来比较新的实现,并且会更好地了解你真正需要的实现有多复杂。

4。一种解决方案或另一种解决方案的性能会更好吗,尤其是在服务器向外扩展的情况下?