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