首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >实现ConcurrentDictionary

实现ConcurrentDictionary
EN

Stack Overflow用户
提问于 2018-11-21 23:55:20
回答 2查看 69关注 0票数 0

我正在尝试为API创建我自己的Cache实现。这是我第一次使用ConcurrentDictionary,我不知道我是否正确地使用了它。在一次测试中,一些东西抛出了错误,到目前为止,我还没有能够再次重现它。也许一些并发专业人员/ ConcurrentDictionary可以查看代码,找出可能的错误。谢谢!

代码语言:javascript
复制
private static readonly ConcurrentDictionary<string, ThrottleInfo> CacheList = new ConcurrentDictionary<string, ThrottleInfo>();

public override void OnActionExecuting(HttpActionContext actionExecutingContext)
{
    if (CacheList.TryGetValue(userIdentifier, out var throttleInfo))
    {
        if (DateTime.Now >= throttleInfo.ExpiresOn)
        {
            if (CacheList.TryRemove(userIdentifier, out _))
            {
                //TODO:
            }
        }
        else
        {
            if (throttleInfo.RequestCount >= defaultMaxRequest)
            {
                actionExecutingContext.Response = ResponseMessageExtension.TooManyRequestHttpResponseMessage();
            }
            else
            {
                throttleInfo.Increment();
            }
        }

    }
    else
    {
        if (CacheList.TryAdd(userIdentifier, new ThrottleInfo(Seconds)))
        {
            //TODO:
        }
    }
}

public class ThrottleInfo
{
    private int _requestCount;

    public int RequestCount => _requestCount;

    public ThrottleInfo(int addSeconds)
    {
        Interlocked.Increment(ref _requestCount);
        ExpiresOn = ExpiresOn.AddSeconds(addSeconds);
    }

    public void Increment()
    {
        // this is about as thread safe as you can get.
        // From MSDN: Increments a specified variable and stores the result, as an atomic operation.
        Interlocked.Increment(ref _requestCount);

        // you can return the result of Increment if you want the new value,
        //but DO NOT set the counter to the result :[i.e. counter = Interlocked.Increment(ref counter);] This will break the atomicity.
    }

    public DateTime ExpiresOn { get; } = DateTime.Now;
}
EN

回答 2

Stack Overflow用户

发布于 2020-04-23 02:48:13

如果我理解您正在尝试做什么,如果ExpiresOn已经通过,则删除条目,否则更新它,如果不存在,则添加。当然,您可以利用AddOrUpdateMethod来简化一些代码。看看这里有一些很好的例子:https://docs.microsoft.com/en-us/dotnet/standard/collections/thread-safe/how-to-add-and-remove-items希望这能有所帮助。

票数 0
EN

Stack Overflow用户

发布于 2020-04-23 07:03:44

只有在以下情况下,ConcurrentDictionary才足以作为线程安全容器:(1)需要保护的整个状态是它的内部状态(它包含的键和值),并且(2)这个状态可以使用它提供的专用API (GetOrAddAddOrUpdate)自动改变。在您的示例中,不满足第二个要求,因为您需要根据它们的值的状态执行remove keys conditionally,而ConcurrentDictionary类不支持这种情况。

所以你当前的缓存实现不是线程安全的。偶尔抛出异常的事实是一个巧合。即使它是完全防抛的,它仍然是非线程安全的,因为它不是完全防错的,这意味着它可能偶尔(或永久地)转换到与其规范不兼容的状态(例如,返回过期的值)。

关于ThrottleInfo类,它有一个可见性错误,如果你在一台机器上大量测试这个类,它可能不会被观察到,然后当你在另一台具有不同CPU架构的机器上部署你的应用程序时,它会突然出现。非volatile private int _requestCount字段是通过公共属性RequestCount公开的,因此(根据C#规范)不能保证所有线程都会看到它的最新值。您可以阅读Igor Ostrovsky的this article,了解内存模型的特性,它可能会让您(像我一样)相信,在多线程代码中使用无锁技术(在本例中使用Interlocked类)会带来比实际价值更多的麻烦。如果你读了它并喜欢它,还有这篇文章的part 2

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53415879

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档