【问题标题】:How to simulate not "safe thread" dictionary behavior?如何模拟非“安全线程”字典行为?
【发布时间】:2013-08-12 07:13:21
【问题描述】:

我试图重现不“保存线程”的字典行为 并实施示例(见下文)。 我预计会出现死锁,但测试工作没有任何问题。 请您帮忙解释一下我的测试中有什么问题以及如何模拟多线程字典错误。

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace parallelTest
{
    [TestClass]
    public class UnitTest1
    {
        Dictionary<int, string> dictionary = new Dictionary<int, string>();
        [TestMethod]
        public void TestMethod1()
        {
            dictionary[2000] = "test";

            Parallel.For(0, 1000, i =>
            {
                string value;
                dictionary.TryGetValue(2000, out value);
                dictionary[2000] = String.Format("new value {0}", i);
                dictionary.Add(i, String.Format("{0}", i));
                Trace.WriteLine(String.Format("thread: {0}, {1}, {2}", Thread.CurrentThread.ManagedThreadId, i, value));
                Thread.Sleep(100);
            }
            );
        }
    }
}

【问题讨论】:

  • 首先删除 Sleep(),但它仍然无法执行您想要的操作。不安全 -> 竞争条件 -> 未定义的行为。您可以预期会出现各种错误,但不会出现死锁。
  • 我在没有睡眠的情况下开始测试:没有任何错误
  • 我的生产代码中有一个“死锁”问题(似乎在 Get from Dictionaty 中)。现在我调查这个问题。
  • 除非您正在锁定,否则您不会出现死锁。此外,你为什么不使用ConcurrentDictionary
  • 在使用 ConcurrentDictionary 之前我想模拟我的问题。

标签: c# multithreading testing dictionary


【解决方案1】:

您不会面临死锁,因为Dictionary 不会锁定其内部数据。
不过,您最终可能会在字典中得到意外的数据。在这种情况下:未添加的项目。

在压力测试之后,您需要检查字典是否符合您的预期。

for(Int32  index=0; index < 1000; index++)
{
    if(dictionary.Values.Any(index.ToString()) ==  false)
    {
          // problem
    }

}

顺便说一句,如果你想强调字典,你需要删除函数中的所有其他操作,例如字符串格式。通过这样做,您将增加在字典中创建问题的机会。

这是调用公共 add 方法时调用的内部方法,您可以看到其中没有锁定。

private void Insert(TKey key, TValue value, bool add)
{
    if (key == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
    }
    if (this.buckets == null)
    {
        this.Initialize(0);
    }
    int num = this.comparer.GetHashCode(key) & 2147483647;
    int num2 = num % this.buckets.Length;
    int num3 = 0;
    for (int i = this.buckets[num2]; i >= 0; i = this.entries[i].next)
    {
        if (this.entries[i].hashCode == num && this.comparer.Equals( this.entries[i].key
                                                                   , key))
        {
            if (add)
            {
                ThrowHelper.ThrowArgumentException(ExceptionResource
                                                      .Argument_AddingDuplicate);
            }
            this.entries[i].value = value;
            this.version++;
            return;
        }
        num3++;
    }
    int num4;
    if (this.freeCount > 0)
    {
        num4 = this.freeList;
        this.freeList = this.entries[num4].next;
        this.freeCount--;
    }
    else
    {
        if (this.count == this.entries.Length)
        {
            this.Resize();
            num2 = num % this.buckets.Length;
        }
        num4 = this.count;
        this.count++;
    }
    this.entries[num4].hashCode = num;
    this.entries[num4].next = this.buckets[num2];
    this.entries[num4].key = key;
    this.entries[num4].value = value;
    this.buckets[num2] = num4;
    this.version++;
    if (num3 > 100 && HashHelpers.IsWellKnownEqualityComparer(this.comparer))
    {
        this.comparer = (IEqualityComparer<TKey>)HashHelpers
                                    .GetRandomizedEqualityComparer(this.comparer);
        this.Resize(this.entries.Length, true);
    }
}

希望对你有帮助。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-17
    • 1970-01-01
    • 1970-01-01
    • 2011-10-20
    • 2011-01-17
    相关资源
    最近更新 更多