"Immutable" 字节数组还是对象锁?
"Immutable" Byte Array or Object Locks?
我正在开发一个多线程 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();
}
多线程应用我写的不多,最近才"discovered"不可变的概念。这是我将上述内容变成不可变 class 的尝试,假设一组设备列表和内存大小在内存被 Initialize
ed 后永远不会改变。 class 是 static
的原因是因为在我的应用程序中只有这个 class 的 1 个实例:
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;
}
}
以下是我对上述问题的疑问:
- 如上所示,我可以锁定二维数组的一行是否正确?
- 我是否正确实现了不可变 class?即,您是否发现
DeviceMemory
class 有任何问题会阻止我从设备线程写入 UpdateBytes
方法并同时从不同线程上的多个客户端读取?
- 与更传统的多维字节 array/lock 相比,这种不可变 class 是否是明智的选择?具体来说,我担心内存 usage/garbage 集合,因为对字节数组的更新实际上是 "new" 字节数组,用于替换对旧数组的引用。然而,对旧数组的引用应在客户端读取后立即释放。每秒大约有 35 台设备更新,每台设备有 4 个客户端以大约 1 秒的间隔读取数据。
- once 解决方案或其他解决方案是否会更好,尤其是在服务器横向扩展的情况下?
感谢阅读!
首先,您在第二个代码示例中显示的不是 "immutable class",而是 "static class"。两种截然不同的东西;您需要复习您的术语,以确保您的沟通有效,并且在研究与这两者相关的技术时不会感到困惑。
关于您的问题:
1.Am I correct that I can lock a row of the 2 dimensional array as shown above?
您应该改用 ReaderWriterLockSlim
,并且您没有显示任何机制来捕获超时异常。但除此之外,是的……您可以为 byte[][]
对象的每个 byte[]
元素使用单独的锁。
更一般地说,您可以使用锁来表示您想要的任何数据单元或其他资源。锁对象不关心。
2.Have I properly implemented an immutable class? i.e. Do you see any issues with the DeviceMemory class that would prevent me from writing to the UpdateBytes method from the device thread and read simultaneously from multiple clients on different thread?
如果你的意思是真的"immutable",那就不是。如果您的意思是 "static",那么是的,但我不清楚知道它是否有用。 class 不是线程安全的,这似乎是您更关心的问题,因此在这方面您做得不对。
就UpdateBytes()
方法本身而言,它本身没有任何问题。实际上,由于将引用从一个变量复制到另一个变量是 .NET 中的原子操作,因此 UpdateBytes()
方法可以 "safely" 更新数组元素,同时其他线程正在尝试检索它。 "Safely" 在某种意义上 reader 不会损坏数据。
但是您的 class 中没有任何内容可以确保 Initialize()
只被调用一次。此外,如果没有同步(锁定或标记变量 volatile
),您无法保证写入一个线程的值将永远被另一个线程观察到。这包括所有字段,以及单个 byte[]
数组元素。
3.Is this immutable class a wise choice over the more traditional multi-dimensional byte array/lock? Specifically, I'm concerned about memory usage/garbage collection since updates to the byte arrays will actually be "new" byte arrays that replace the reference to the old array. The reference to the old array should be released immediately after the client reads it, however. There will be about 35 devices updating every second and 4 clients per device reading at approximately 1 second intervals.
您的问题中没有足够的上下文来进行比较,因为我们不知道您将如何访问 memoryBlock
数组。如果您只是将新数组复制到数组中,则两者应该相似。即使仅在第二个示例中创建新数组,然后假设数组不大,我希望每秒生成约 100 个新对象完全在垃圾收集器的带宽内。
至于你的第二个代码示例中的方法是否是一种理想的方法,我会非常尊重地建议你应该坚持使用传统的同步代码(即使用 ReaderWriterLockSlim
,或者甚至只是一个普通的 lock
语句)。并发很难用常规锁来正确处理,更不用说尝试编写无锁代码了。考虑到您描述的更新率,我希望简单的 lock
语句可以正常工作。
如果您 运行 遇到一些带宽问题,那么至少您有一个已知良好的实现来比较新的实现,并且会更好地了解您真正需要的实现有多复杂。
4.Would 一旦解决方案或其他解决方案表现更好,特别是如果服务器要横向扩展?
不可能没有更多细节。
我正在开发一个多线程 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();
}
多线程应用我写的不多,最近才"discovered"不可变的概念。这是我将上述内容变成不可变 class 的尝试,假设一组设备列表和内存大小在内存被 Initialize
ed 后永远不会改变。 class 是 static
的原因是因为在我的应用程序中只有这个 class 的 1 个实例:
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;
}
}
以下是我对上述问题的疑问:
- 如上所示,我可以锁定二维数组的一行是否正确?
- 我是否正确实现了不可变 class?即,您是否发现
DeviceMemory
class 有任何问题会阻止我从设备线程写入UpdateBytes
方法并同时从不同线程上的多个客户端读取? - 与更传统的多维字节 array/lock 相比,这种不可变 class 是否是明智的选择?具体来说,我担心内存 usage/garbage 集合,因为对字节数组的更新实际上是 "new" 字节数组,用于替换对旧数组的引用。然而,对旧数组的引用应在客户端读取后立即释放。每秒大约有 35 台设备更新,每台设备有 4 个客户端以大约 1 秒的间隔读取数据。
- once 解决方案或其他解决方案是否会更好,尤其是在服务器横向扩展的情况下?
感谢阅读!
首先,您在第二个代码示例中显示的不是 "immutable class",而是 "static class"。两种截然不同的东西;您需要复习您的术语,以确保您的沟通有效,并且在研究与这两者相关的技术时不会感到困惑。
关于您的问题:
1.Am I correct that I can lock a row of the 2 dimensional array as shown above?
您应该改用 ReaderWriterLockSlim
,并且您没有显示任何机制来捕获超时异常。但除此之外,是的……您可以为 byte[][]
对象的每个 byte[]
元素使用单独的锁。
更一般地说,您可以使用锁来表示您想要的任何数据单元或其他资源。锁对象不关心。
2.Have I properly implemented an immutable class? i.e. Do you see any issues with the DeviceMemory class that would prevent me from writing to the UpdateBytes method from the device thread and read simultaneously from multiple clients on different thread?
如果你的意思是真的"immutable",那就不是。如果您的意思是 "static",那么是的,但我不清楚知道它是否有用。 class 不是线程安全的,这似乎是您更关心的问题,因此在这方面您做得不对。
就UpdateBytes()
方法本身而言,它本身没有任何问题。实际上,由于将引用从一个变量复制到另一个变量是 .NET 中的原子操作,因此 UpdateBytes()
方法可以 "safely" 更新数组元素,同时其他线程正在尝试检索它。 "Safely" 在某种意义上 reader 不会损坏数据。
但是您的 class 中没有任何内容可以确保 Initialize()
只被调用一次。此外,如果没有同步(锁定或标记变量 volatile
),您无法保证写入一个线程的值将永远被另一个线程观察到。这包括所有字段,以及单个 byte[]
数组元素。
3.Is this immutable class a wise choice over the more traditional multi-dimensional byte array/lock? Specifically, I'm concerned about memory usage/garbage collection since updates to the byte arrays will actually be "new" byte arrays that replace the reference to the old array. The reference to the old array should be released immediately after the client reads it, however. There will be about 35 devices updating every second and 4 clients per device reading at approximately 1 second intervals.
您的问题中没有足够的上下文来进行比较,因为我们不知道您将如何访问 memoryBlock
数组。如果您只是将新数组复制到数组中,则两者应该相似。即使仅在第二个示例中创建新数组,然后假设数组不大,我希望每秒生成约 100 个新对象完全在垃圾收集器的带宽内。
至于你的第二个代码示例中的方法是否是一种理想的方法,我会非常尊重地建议你应该坚持使用传统的同步代码(即使用 ReaderWriterLockSlim
,或者甚至只是一个普通的 lock
语句)。并发很难用常规锁来正确处理,更不用说尝试编写无锁代码了。考虑到您描述的更新率,我希望简单的 lock
语句可以正常工作。
如果您 运行 遇到一些带宽问题,那么至少您有一个已知良好的实现来比较新的实现,并且会更好地了解您真正需要的实现有多复杂。
4.Would 一旦解决方案或其他解决方案表现更好,特别是如果服务器要横向扩展?
不可能没有更多细节。