【问题标题】:Hierarchical locking strategy of entries in a ConcurrentDictionary and the dictionary itself in C#ConcurrentDictionary 中条目的分层锁定策略和 C# 中的字典本身
【发布时间】:2016-08-19 17:03:43
【问题描述】:

我不得不在我的代码中使用并发字典,其中“键”是一个对象,“值”是一个 int,表示第三方库提供的与所述对象的唯一连接参数。存储连接参数,因为当通过第三方库引发事件时,唯一键是事件参数之一,我们可以使用它找到正确的对象来处理进一步的代码。

我遇到的问题如下:

  • 有时,并发字典需要使用刷新的需要处理的对象列表进行更新。

  • 各个对象在处理过程中需要锁定,因为抛出的事件必须按顺序执行,而不是并行执行。 (这已成为最近的要求,因为我们看到任务有时在不同线程上并行处理,并在保存到数据库时得到 OptimisticConcurrencyException。)

对字典中的单个对象应用锁定不是问题,但是当我尝试从字典中删除当前锁定的对象同时刷新列表时会出现新问题。

我尝试编写一个小型控制台应用程序,它可以按顺序执行有问题的步骤,即“Monitor.Exit(key);”行抛出异常“对象同步方法是从未同步的代码块中调用的。”

class Program
{
    static ConcurrentDictionary<object, int> _handles = new ConcurrentDictionary<object, int>();

    static void Main(string[] args)
    {
        object lockableKey = 'key';

        _handles.AddOrUpdate(lockableKey, 10, (oldkey, oldvalue) => 10);
        var key = _handles.Keys.FirstOrDefault(h => h == lockableKey);

        Console.WriteLine("key found, beginning to lock");
        Monitor.Enter(key);
        Console.WriteLine("key locked");

        ////Console.WriteLine("applying outer lock, hoping for code to stop executing");
        ////Monitor.Enter(_handles);

        Console.WriteLine("Removing key from dictionary");
        int dummy;
        _handles.TryRemove(lockableKey, out dummy);

        Console.WriteLine("Removing lock from object removed from dictionary");
        Monitor.Exit(key);
    }

所以问题是,有没有一种方法可以将锁应用于外部字典,其作用类似于主锁,并等待字典内对象上的所有子锁完成执行,然后再刷新字典并继续处理传入的活动?

【问题讨论】:

  • 您的代码令人困惑。您正在使用lockableKey(设置为“key”,应该用双引号编写)从字典中检索自己(意思是:key 只能具有值“key”或null,因为您正在使用FirstOrDefault)。然后您尝试通过值“key”(或null,这将引发异常)锁定。不能保证第一个“key”等于字典中的“key”;它们都可以是相同值的不同引用。您正在从字典中删除“密钥”,然后尝试解锁。为什么不先打电话:TryRemove(theKey)
  • 该示例不是实际实现的代码,而是我设想的事件顺序的序列示例(这将发生在不同的线程中)。这只是为了证明我面临的问题,即我可能会遇到异常“对象同步方法是从不同步的代码块中调用的”。因此需要外锁和实际问题。
  • 您是否考虑过在单个线程中执行字典更新的可能性?比如制作单线程调度器并在这个调度器上使用Task.Factory.StartNew重载创建更新任务?

标签: c# multithreading


【解决方案1】:

感谢大家的帮助,看来之前的评论被删除了,帮助我找到了答案,所以我不能给予应得的功劳。

我已经实现了'ReaderWriterLockSlim' class。这允许多个读取器线程同时处理,但只有一个写入器线程。所以所有的处理线程都取出一个读取器锁,但是更新字典的线程取出一个写入器锁。写锁等待所有读锁完成,但也优先于所有读锁。因此,一旦请求写入器锁,读取器锁必须等到写入器完成后才能进入临界区。

除此之外,在阅读器线程中,我还将 Monitor.TryLock 应用于字典中的实际对象,以防止两个线程同时处理同一个项目。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-29
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多