gpt4 book ai didi

c# - 如何仅在 ConcurrentDictionary 存在的情况下通过键原子更新 ConcurrentDictionary 中的值

转载 作者:太空狗 更新时间:2023-10-29 20:42:23 27 4
gpt4 key购买 nike

ConcurrentDictionary.TryUpdate 方法需要与具有指定键的元素的值进行比较的 comparisonValue。但是如果我想做这样的事情:

if (!_store.TryGetValue(book.Id, out Book existing))
{
throw new KeyNotFoundException();
}

if (!_store.TryUpdate(book.Id, book, existing))
{
throw new Exception("Unable to update the book");
}

当多个线程同时更新一本书时,它会抛出异常,因为 existing 书在另一个线程中被更改了。

我不能使用索引器,因为它会添加不存在的书,而且我无法检查键是否存在,因为它也不是原子的。

我的代码是这样改的:

while (true)
{
if (!_store.TryGetValue(book.Id, out Book existing))
{
throw new KeyNotFoundException();
}

if (_store.TryUpdate(book.Id, book, existing))
{
break;
}
}

但我担心无限循环。

但是如果我将在 Update 和 Delete 方法上使用锁定,我将失去使用 ConcurrentDictionary 的优势。

解决我的问题的正确方法是什么?

最佳答案

这可以通过添加能够替换值的包装器来完成。为了简化代码,我将使用锁来实现这个包装器(以避免双值构造)。

首先 - 界面。请检查它是否反射(reflect)了所需的操作。我使用 int 类型作为键,使用 string 作为值只是为了简化示例。

    public delegate TValue GetNewValue<TValue>(TValue previousValue);

public interface IIntStringAtomicDictionary
{
/// <returns>true if was added, otherwise false</returns>
bool AddIfMissingOnly(int key, Func<string> valueGetter);

/// <returns>true if was updated, otherwise false</returns>
bool UpdateIfExists(int key, GetNewValue<string> convertPreviousValueToNew);
}

执行如下。它无法删除值(value),可以很简单地完成(如果需要,我可以更新答案)

    public sealed class IntStringAtomicDictionary : IIntStringAtomicDictionary
{
private readonly ConcurrentDictionary<int, ValueWrapper<string>> _innerDictionary = new ConcurrentDictionary<int, ValueWrapper<string>>();
private readonly Func<int, ValueWrapper<string>> _wrapperConstructor = _ => new ValueWrapper<string>();

public bool AddIfMissingOnly(int key, Func<string> valueGetter)
{
var wrapper = _innerDictionary.GetOrAdd(key, _wrapperConstructor);

return wrapper.AddIfNotExist(valueGetter);
}

public bool UpdateIfExists(int key, GetNewValue<string> convertPreviousValueToNew)
{
var wrapper = _innerDictionary.GetOrAdd(key, _wrapperConstructor);

return wrapper.AddIfExists(convertPreviousValueToNew);
}
}

private sealed class ValueWrapper<TValue> where TValue : class
{
private readonly object _lock = new object();
private TValue _value;

public bool AddIfNotExist(Func<TValue> valueGetter)
{
lock (_lock)
{
if (_value is null)
{
_value = valueGetter();

return true;
}

return false;
}
}

public bool AddIfExists(GetNewValue<TValue> updateValueFunction)
{
lock (_lock)
{
if (!(_value is null))
{
_value = updateValueFunction(_value);

return true;
}

return false;
}
}
}

写完代码我们可以重新阅读需求。据我了解,我们必须应用以下内容:

  • 不同的键应该是来自不同线程的更新而不加锁。
  • 值更新应该是原子的
  • 如果禁止平行增值 - 如有错误请指出
  • 应该能够从不同的线程创建不同的值。

由于“并行增值”的限制,我们必须锁定值(value)创造。因此我上面的包装器有这个锁。

所有其他操作不使用任何锁。

其他改进:

  • ValueWrapper 类可以使用 ReadWriteLockSlim 允许并行读取值。
  • 可以使用相同的锁删除值。当然,我们可以在这里设置竞争条件。

关于c# - 如何仅在 ConcurrentDictionary 存在的情况下通过键原子更新 ConcurrentDictionary 中的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52745296/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com