【问题标题】:What is the correct approach when trying to remove some items in ConcurrentDictionary尝试删除 ConcurrentDictionary 中的某些项目时的正确方法是什么
【发布时间】:2015-03-18 14:18:58
【问题描述】:

这样更好吗:

    public void Test()
    {

        ConcurrentDictionary<int, string> dictionary = new ConcurrentDictionary<int, string>();

        dictionary.TryAdd(0, "A");
        dictionary.TryAdd(1, "B");
        dictionary.TryAdd(2, "A");
        dictionary.TryAdd(3, "D");

        foreach (var item in dictionary)
        {
            string foundItem;

            if (dictionary.TryGetValue(item.Key, out foundItem))
            {
                if (foundItem == "A")
                {
                    if (dictionary.TryRemove(item.Key, out foundItem))
                    {
                        // Success
                    }
                }
            }
        }

    }  

比这个?:

    public void Test2()
    {

        ConcurrentDictionary<int, string> dictionary = new ConcurrentDictionary<int, string>();

        dictionary.TryAdd(0, "A");
        dictionary.TryAdd(1, "B");
        dictionary.TryAdd(2, "A");
        dictionary.TryAdd(3, "D");

        foreach (var item in dictionary)
        {
            string foundItem;

            if (item.Value == "A")
            {
                if (dictionary.TryRemove(item.Key, out foundItem))
                {
                    // Success
                }
            }
        }

    }  

这个方法会被多个线程访问。

我的困惑是,每当我想删除一个项目时,我都会尝试先获取它,然后再删除它。但首先,我使用了foreach 循环,这意味着我已经得到了该项目。任何想法将不胜感激。

【问题讨论】:

  • 第一个选项没有意义。当你已经有一件物品时,你为什么还要打电话给TryGetValue:?
  • 我使用TryGetValue 因为我在想我可能会得到一个过时的值... :)
  • 如果某个线程在您调用 TryGetValue 之后但在您调用 TryRemove 之前修改了值怎么办?仍然有一场比赛:)
  • 我认为TryGeValue 会阻止其他方法,例如TryRemove。不是这样吗?
  • 它可能在内部锁定(或无锁定)。但是我说的是当某个线程在TryGeValue 返回之后,但在调用TryRemove 之前修改值时会发生什么。想想看。我会选择第二个选项或 Jon 在下面的答案,

标签: c# dictionary concurrency


【解决方案1】:

我认为第一种方法没有任何好处。我只是使用 LINQ 来查找项目:

foreach (var entry in dictionary.Where(e => e.Value == "A"))
{
    string ignored;
    // Do you actually need to check the return value?
    dictionary.TryRemove(entry.Key, out ignored);
}

当然,您需要考虑如果另一个线程添加一个值为"A" 的新条目或更新现有条目(可能使值"A",也可能使值不是-@),您希望发生什么987654324@ 在您进行迭代时...是否删除该条目对您有影响吗?不能保证会发生什么。(迭代器不拍摄快照,但不能保证完全返回 -最新数据。)

可能想通过之后检查我调用的变量ignored 来检查您删除的值是否真的是"A"。这真的取决于你的上下文。当您有多个线程修改地图时,您需要考虑任何事情都可能随时发生 - 在您的代码实际执行的操作中。

还有一个事实是,您实际上不得不在整个字典中搜索...您是否在其他地方按键查找?

【讨论】:

  • 这让我松了一口气,乔恩。我想我误解了这个集合的锁定机制。
  • 嗨乔恩,如果我这样做,dictionary.TryGetValue(entry.Key, out ignored); 然后检查是否ignored == "A" 然后执行dictionary.TryRemove(entry.Key, out ignored); 将确保我没有得到过时的值吗?如果否,那么我如何确保获得最新值?
  • @fiberOptics:不,它不会 - 因为在TryGet 之后但在TryRemove 之前之后 仍有可能改变值。根据我的回答,如果您想检查您删除的条目的值是否为 "A",请使用 TryRemove 的 out 参数。
  • 我认为这是唯一的选择。谢谢乔恩。
  • @JonSkeet 当然,这引出了一个问题,如果你发现你删除了一个值不是“A”的项目,你应该怎么做?你再把它加回去吗?如果其他人在您删除它之后和您将其添加回来之前尝试添加/更新该值,或者如果有人在您删除它之后并且在您重新添加它之前尝试实际删除它(例如,真实地)怎么办?
【解决方案2】:

第二种方法听起来不错。如果您有多个线程尝试删除项目,其中一些线程会在 TryRemove 中失败,但有一个线程会成功,这应该很好。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-06-13
    • 2013-10-16
    • 2017-06-11
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 2019-12-15
    相关资源
    最近更新 更多