访问MemoryStream的不同线程
有一些代码通过调用GetBuffer()将数据直接写入MemoryStream对象的数据缓冲区。 它还适当地使用和更新Position和SetLength()属性。
此代码在99.9999%的时间内正常工作。 从字面上看。 只有每隔几十万次的迭代它才会出现。 具体问题是MemoryStream的Position属性突然返回零而不是适当的值。
但是,添加了检查0的代码并抛出exception,其中包括在单独的方法中记录MemoryStream属性(如位置和长度)。 那些返回正确的值。 在同一方法中进一步添加日志记录表明,当出现这种罕见情况时,Position在此特定方法中仅为零。
好的。 显然,这必须是一个线程问题。 而且很可能是编译器优化问题。
但是,这个软件的本质是它由调度程序的“任务”组织,因此几个实际的操作系统线程中的任何一个都可以在任何给定时间运行此代码 – 但一次不得超过一个。
所以我的猜测通常会发生同样的线程不断用于此方法,然后在极少数情况下使用不同的线程。 (只需编写想法,通过捕获和比较线程ID来测试这个理论。)
然后由于编译器优化,不同的线程永远不会得到正确的值。 它得到一个“陈旧”的价值。
通常在这种情况下,我会将“volatile”关键字应用于相关变量,以查看是否可以修复它。 但在这种情况下,变量位于MemoryStream对象中。
有没有人有任何其他想法? 或者这是否意味着我们必须实现自己的MemoryStream对象?
真诚的,韦恩
编辑:只运行一个测试,计算此方法的调用总数,并计算ManagedThreadId与上次调用的次数不同的次数。 它几乎完全是50%的时间切换线程 – 在它们之间交替。 所以我上面的理论几乎肯定是错的,或者错误会更频繁地发生。
编辑:这个错误很少发生,它需要将近一个星期没有错误运行之前感到有任何信心,它真的消失了。 相反,最好运行实验来确切地确定问题的本质。
编辑:当前锁定是通过使用MemoryStream的5种方法中的每一种中的lock()语句来处理的。
(真的需要示例代码来证实这一点。)
MemoryStream
成员未记录为线程安全(例如Position
),因此您需要确保一次只能从一个线程访问此实例(或对逻辑上对象的任何引用)。
但是MemoryStream
没有记录为具有线程关联,因此您可以从不同的线程访问实例 – 只要这种访问不是并发的。
线程很难(这个Q&A的公理)。
我建议你进行一些并发访问,两个线程同时访问同一个实例,这偶尔会破坏实例状态的某些方面。
我会确保尽可能简单地保持锁定(试图更加聪明,限制锁定通常是很难找到错误的原因)并让事情变得有效。 在多核系统上进行测试也可能有所帮助。 如果分析显示存在显着的净(应用程序整体)增益的潜力,则仅尝试并优化锁定。