【问题标题】:Using Concurrent​Bag<​T> without allowing duplicates使用 Concurrent​Bag<​T> 且不允许重复
【发布时间】:2017-06-14 17:31:13
【问题描述】:

我正在考虑使用 Concurrent​Bag,但我不想在我的收藏中允许重复。我的想法是,由于检查和添加到集合之间的“步骤”,这样做不会是线程安全的:

    private ConcurrentBag<IClientCallback> _callbackChannels = new ConcurrentBag<IClientCallback>();
...
    public void AddCallback(IClientCallback callback) {
        if (!_callbackChannels.Contains(callback))
        {
            _callbackChannels.Add(callback);
        }
    }

所以我在想这样的事情,但也许这是一个糟糕的想法?

        _callbackChannels = new ConcurrentBag<IClientCallback>(_callbackChannels.Union(new List<IClientCallback>() { callback }));

所以,我的问题是:这会比检查添加方法更安全吗?有什么我遗漏的,有没有更好的方法?

我们的想法是,理论上 AddCallback 可能会使用相同的回调通道对象进行多次调用 - 我知道这本身很糟糕,但我还是想处理它。

【问题讨论】:

  • 只需使用ConcurrentDictionary&lt;IClientCallback, object&gt;
  • @gakera 你考虑过使用 ConcurrentDictionary 吗?
  • ConcurrentDictionary 是要走的路。
  • @Fabio 嗯,我没想到那样用它,那可能会更好,谢谢
  • 具体来说,使用ConcurrentDictionary&lt;IClientCallback, byte&gt;,因为与使用对象作为值相比,这也会减少分配。

标签: c# multithreading concurrency


【解决方案1】:

您可以在添加到包之前锁定。

private ConcurrentBag<IClientCallback> _callbackChannels = new ConcurrentBag<IClientCallback>();

 private object _lockObject = new Object();

    public void AddCallback(IClientCallback callback) 
    {
        lock(_lockObject)
        {
            if (!_callbackChannels.Contains(callback))
            {
                _callbackChannels.Add(callback);
            }
        }
    }

这可能会很昂贵,因为每个线程都会在较早的线程完成任务之前等待锁定。

【讨论】:

  • 那你就不需要ConcurrentBag,直接用HashSet就可以了
  • 也不需要第二个对象,你可以锁定集合本身
  • @Servy 我总是被教导使用显式锁定对象;直接锁定集合不被认为是不好的形式吗?不过,你显然比我更有权威。
  • @BradleyUffner 您不应该锁定可以从任何比您需要的更大范围访问的对象,或者该对象正在更改(除非您想更改您锁定的对象)。如果您没有一个不会更改且具有适当范围的对象(通常不会更改),那么您创建一个。如果您已经有一个,那么没有理由创建 另一个
猜你喜欢
  • 1970-01-01
  • 2012-02-03
  • 2014-12-13
  • 2018-05-19
  • 1970-01-01
  • 1970-01-01
  • 2015-04-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多