c# - C# 字典的原子 AddOrUpdate

标签 c# dictionary atomic

假设如下代码:

if (myDictionary.ContainsKey(aKey))
    myDictionary[aKey] = aValue;
else
    myDictionary.Add(aKey, aValue);

这段代码访问字典两次,一次是判断aKey是否存在,另一次是更新(如果存在)或者添加(如果不存在)。我想当这段代码只执行几次时,这种方法的性能是“可以接受的”。但是,在我的应用程序中,类似的代码大约执行了 50 万次。我分析了我的代码,它显示 80% 的 CPU 时间花在了这部分(见下图),因此这激发了改进。

请注意,字典是 lambdas

第一个解决方法很简单:

myDictionary[aKey] = aValue;

如果 aKey 存在,它的值被替换为 aValue;如果不存在,则将以aKey 为键,以aValue 为值的KeyValuePair 添加到myDictionary。但是,这种方法有两个缺点:

首先,您不知道 aKey 是否存在,这会阻止您进行其他逻辑。例如,您不能根据此解决方法重写以下代码:

int addCounter = 0, updateCounter = 0;
if (myDictionary.ContainsKey(aKey))
{
    myDictionary[aKey] = aValue;
    addCounter++;
}
else
{
    myDictionary.Add(aKey, aValue);
    updateCounter++;
}

其次,更新不能是旧值的函数。例如,您不能执行类似于以下的逻辑:

if (myDictionary.ContainsKey(aKey))    
    myDictionary[aKey] = (myDictionary[aKey] * 2) + aValue;    
else    
    myDictionary.Add(aKey, aValue);

第二种解决方法是使用ConcurrentDictionary。很明显,通过使用 delegates,我们可以解决上述第二个问题;然而,我仍然不清楚我们如何解决第一个问题。

提醒一下,我关心的是加快速度。鉴于只有一个线程使用此过程,我认为仅一个线程的并发(带锁)的代价不值得使用 ConcurrentDictionary

我漏掉了一点吗?谁有更好的建议?

最佳答案

如果您真的想要 ConcurrentDictionary 中的 AddOrUpdate 方法,但没有使用它的性能影响,您将必须自己实现这样的 Dictionary。

好消息是,由于 CoreCLR 是开源的,您可以从 CoreCLR repository 获取实际的 .Net 字典源。并应用您自己的修改。看起来不会那么难,看看那里的Insert私有(private)方法。

一个可能的实现是(未经测试):

public void AddOrUpdate(TKey key, Func<TKey, TValue> adder, Func<TKey, TValue, TValue> updater) {

    if( key == null ) {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
    }

    if (buckets == null) Initialize(0);
    int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
    int targetBucket = hashCode % buckets.Length;

    for (int i = buckets[targetBucket]; i >= 0; i = entries[i].next) {
        if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) {
            entries[i].value = updater(key, entries[i].value);
            version++;
            return;
        } 

    }
    int index;
    if (freeCount > 0) {
        index = freeList;
        freeList = entries[index].next;
        freeCount--;
    }
    else {
        if (count == entries.Length)
        {
            Resize();
            targetBucket = hashCode % buckets.Length;
        }
        index = count;
        count++;
    }

    entries[index].hashCode = hashCode;
    entries[index].next = buckets[targetBucket];
    entries[index].key = key;
    entries[index].value = adder(key);
    buckets[targetBucket] = index;
    version++;

}

关于c# - C# 字典的原子 AddOrUpdate,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33732656/

相关文章:

c# - 如何在带有 SSL 的 C# 中使用 HTTP GET 请求? (违反协议(protocol))

python - 如何将嵌套的 OrderedDict 转换为字典?

Scala Spark - map 的DataFrame列上的空 map (String,Int)

c++ - gcc-c++ 是否没有为当前的 x86-64 处理器优化原子操作

c++ - 在 MPI 中对 cout 的 "atomic"调用

file - 是否有一种独立于操作系统的方式来自动覆盖文件?

c# - 如何让两个文本框数据接受 jQuery 数据选择器插件?

javascript - SPA - Firebase 和 .Net WebApi 2 身份验证

c# - 从前面带有分隔符的文本中删除单词(使用正则表达式)

python - 将键列表转换为嵌套字典