我正在开发一个无锁堆栈和队列数据结构,我可以在其中放置尽可能多的项目并在一次调用中收集所有项目,我认为我的设计是可靠的并且它按预期工作直到我开始我认为在纯粹的C#环境中我不可能发现意外的异常:
托管调试助手’FatalExecutionEngineError’检测到问题附加信息:运行时遇到致命错误.错误的地址位于线程0x16f0处的0x6caf6ac7处.错误代码是0xc0000005.此错误可能是CLR中的错误,也可能是用户代码的不安全或不可验证部分中的错误.此错误的常见来源包括COM-interop或PInvoke的用户编组错误,这可能会破坏堆栈.
我似乎无法找到它发生的方式,并想知道是否有人可以指导我找出错误的原因:
这是stackbulkcollector的实现:
public class StackBulkCollector<T> : IBulkCollector<T> { class Node { public T value; private bool m_isSet; private Node m_prev; public Node(T data) { value = data; } public Node() { } public Node Prev { set { m_prev = value; m_isSet = true; } get { if (!m_isSet) { SpinWait s = new SpinWait(); while (!m_isSet) { s.SpinOnce(); } } return m_prev; } } } class Enumerable : IEnumerable<T> { private Node m_last; public Enumerable(Node last) { m_last = last; } public IEnumerator<T> GetEnumerator() { return new Enumerator(m_last); } System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { return new Enumerator(m_last); } } class Enumerator : IEnumerator<T> { private readonly Node m_last; private Node m_current; public Enumerator(Node last) { var node = new Node(); m_current = m_last = node; node.Prev = last; } public T Current { get { return m_current.value; } } public void dispose() { } object System.Collections.IEnumerator.Current { get { return this; } } public bool MoveNext() { if (m_current == null) { return false; } m_current = m_current.Prev; return m_current != null; } public void Reset() { m_current = m_last; } } private Node m_last; public void Add(T data) { var node = new Node(data); node.Prev = Interlocked.Exchange(ref m_last,node); } public IEnumerable<T> GetBulk() { var last = Interlocked.Exchange(ref m_last,null); return new Enumerable(last); } }
这是我用来测试它的测试程序:
class Program { public static readonly int UseThreads = 4; public static readonly TimeSpan Duration = new TimeSpan(0,3); public static long[] AddedItems = new long[UseThreads]; static void Main(string[] args) { IBulkCollector<TestData> bulkCollector = new QueuedBulkCollector<TestData>(); while (true) { using (var countdownEvent = new CountdownEvent(UseThreads + 1)) { var results = new Dictionary<int,List<TestData>>(); if (bulkCollector is QueuedBulkCollector<TestData>) { bulkCollector = new StackBulkCollector<TestData>(); Console.WriteLine("Starting StackBulkCollector Test"); } else { bulkCollector = new StackBulkCollector<TestData>(); Console.WriteLine("Starting QueuedBulkCollector Test"); } for (int i = 0; i < UseThreads; i++) { results[i] = new List<TestData>(); Thread t = new Thread(PushTestData); t.Start(new object[] {i,bulkCollector,countdownEvent}); } var start = DateTime.Now; Thread readerThred = new Thread(() => { while ((DateTime.Now - start) < (Duration - new TimeSpan(0,100))) { foreach (var testData in bulkCollector.GetBulk()) { results[testData.Id].Add(testData); } //Console.WriteLine("Doing Some Read {0}",currBulk.Count); System.Threading.Thread.Sleep(10); } countdownEvent.Signal(); }); readerThred.Start(); countdownEvent.Wait(); var lastBulk = bulkCollector.GetBulk().ToList(); foreach (var testData in lastBulk) { results[testData.Id].Add(testData); } Console.WriteLine("Doing Last Read {0}",lastBulk.Count); long[] value = new long[UseThreads]; long totalCount = 0; int errorCount = 0; for (int i = 0; i < UseThreads; i++) { value[i] = AddedItems[i]; totalCount += value[i]; Console.WriteLine("Thread {0} Push {1} Items",i,value[i]); var verifyArray = results[i].OrderBy(p => p.Value).ToList(); if (verifyArray.Count != value[i]) { Console.WriteLine("Not Working Count miss match"); errorCount++; } else { var expected = 0; foreach (var testData in verifyArray) { if (expected != testData.Value) { Console.WriteLine("NotWorking"); errorCount++; } expected++; } } } Console.WriteLine("Done Total Push {0} with {1} errors.",totalCount.ToString("#,##0"),errorCount); if (errorCount != 0) { Console.ReadKey(); } } } } private static void PushTestData(object o) { object[] parms = o as object[]; int id = (int)parms[0]; IBulkCollector<TestData> bulkCollector = (IBulkCollector<TestData>)parms[1]; CountdownEvent endEvetn = (CountdownEvent)parms[2]; AddedItems[id] = 0; var start = DateTime.Now; while ((DateTime.Now - start) < Duration) { bulkCollector.Add(new TestData() { Id = id,Value = AddedItems[id]}); AddedItems[id]++; } endEvetn.Signal(); } }
任何建议都将受到欢迎.
解决方法
对不起,有缺陷的内存模块是问题的原因,它已被替换,问题解决了.