【问题标题】:Tentative locks in C#?C#中的暂定锁?
【发布时间】:2016-06-10 15:15:31
【问题描述】:

假设我想允许并行执行一些代码,但需要其他代码等待所有这些操作完成。

让我们想象一个softlock 除了lock

public static class MySimpleCache
{
    private static readonly SynchronizedCollection<KeyValuePair<string, string>> Collection = new SynchronizedCollection<KeyValuePair<string, string>>();

    public static string Get(string key, Func<string> getter)
    {
        // Allow parallel enumerations here,
        // but force modifications to the collections to wait. 
        softlock(Collection.SyncRoot)
        {
            if (Collection.Any(kvp => kvp.Key == key))
            {
                return Collection.First(kvp => kvp.Key == key).Value;
            }
        }

        var data = getter();

        // Wait for previous soft-locks before modifying the collection and let subsequent softlocks wait
        lock (Collection.SyncRoot)
        {
            Collection.Add(new KeyValuePair<string, string>(key, data));
        }
        return data;
    }
}

C#/.NET 中是否有任何设计模式或语言/框架功能可以以一种简单可靠的方式实现这一点,还是必须从头开始实现这一点?

我目前仅限于 .NET 3.5,并且我最感兴趣的是概念问题,而不是其他可能解决示例本身的可能集合。

【问题讨论】:

  • 听起来你需要ReaderWriterLockSlim,它可以从读取器锁开始,然后升级为写入器锁。
  • 您的查找效率略低,您枚举了两次,您可以使用var result = Collection.FirstOrDefault(kvp =&gt; kvp.Key == key); if(result != default(KeyValuePair&lt;string, string&gt;)) { return result.Value; } 并且只查找一次。
  • @MatthewWatson 谢谢,看起来很合适,我会玩弄它。
  • 哎呀!= 不起作用,但我正在写一个答案(使用 ReaderWriterLockSlim),它也有一个固定版本。
  • @ScottChamberlain 虽然那时我通常无法区分集合是否不包含该值,或者它包含的值是否为空。

标签: c# .net .net-3.5 locking


【解决方案1】:

在这种情况下,您可以使用ReaderWriterLockSlim,它会允许多个阅读器,直到有人想写,然后它会阻止所有阅读器,只允许单个作者通过。

public static class MySimpleCache
{
    private static readonly SynchronizedCollection<KeyValuePair<string, string>> Collection = new SynchronizedCollection<KeyValuePair<string, string>>();
    private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();

    public static string Get(string key, Func<string> getter)
    {
        //This allows multiple readers to run concurrently.
        Lock.EnterReadLock();
        try
        {
            var result = Collection.FirstOrDefault(kvp => kvp.Key == key);
            if (!Object.Equals(result, default(KeyValuePair<string, string>)))
            {
                return result.Value;
            }
        }
        finally
        {
            Lock.ExitReadLock();
        }


        var data = getter();

        //This blocks all future EnterReadLock(), once all finish it allows the function to continue
        Lock.EnterWriteLock();
        try
        {
            Collection.Add(new KeyValuePair<string, string>(key, data));
            return data;
        }
        finally
        {
            Lock.ExitWriteLock();
        }
    }
}

但是,您可能想检查一下,当您等待写锁时,其他人可能已将记录输入到缓存中,在这种情况下,您可以使用EnterUpgradeableReadLock(),这允许无限的人在EnterReadLock() 内部,但升级锁只能有一个人(并且仍然不会有写锁)。当您知道自己可能会写但有机会不写时,可升级锁很有用。

public static class MySimpleCache
{
    private static readonly SynchronizedCollection<KeyValuePair<string, string>> Collection = new SynchronizedCollection<KeyValuePair<string, string>>();
    private static readonly ReaderWriterLockSlim Lock = new ReaderWriterLockSlim();

    public static string Get(string key, Func<string> getter)
    {
        //This allows multiple readers to run concurrently.
        Lock.EnterReadLock();
        try
        {
            var result = Collection.FirstOrDefault(kvp => kvp.Key == key);
            if (!Object.Equals(result, default(KeyValuePair<string, string>)))
            {
                return result.Value;
            }
        }
        finally
        {
            Lock.ExitReadLock();
        }

        //This allows unlimited EnterReadLock to run concurrently, but only one thread can be in upgrade mode, other threads will block.
        Lock.EnterUpgradeableReadLock();
        try
        {
            //We need to check to see if someone else filled the cache while we where waiting.
            var result = Collection.FirstOrDefault(kvp => kvp.Key == key);
            if (!Object.Equals(result, default(KeyValuePair<string, string>)))
            {
                return result.Value;
            }


            var data = getter();

            //This blocks all future EnterReadLock(), once all finish it allows the function to continue
            Lock.EnterWriteLock();
            try
            {
                Collection.Add(new KeyValuePair<string, string>(key, data));
                return data;
            }
            finally
            {
                Lock.ExitWriteLock();
            }
        }
        finally
        {
            Lock.ExitUpgradeableReadLock();
        }
    }
}

P.S. 您在评论中提到该值可能为 null,因此 FirstOrDefault() 不起作用。在这种情况下,请使用扩展方法来创建 TryFirst() 函数。

public static class ExtensionMethods
{
    public static bool TryFirst<T>(this IEnumerable<T> @this, Func<T, bool> predicate, out T result)
    {
        foreach (var item in @this)
        {
            if (predicate(item))
            {
                result = item;
                return true;
            }
        }
        result = default(T);
        return false;
    }
}

//Used like
Lock.EnterReadLock();
try
{
    KeyValuePair<string, string> result;
    bool found = Collection.TryFirst(kvp => kvp.Key == key, out result);
    if (found)
    {
        return result.Value;
    }
}
finally
{
    Lock.ExitReadLock();
}

【讨论】:

    猜你喜欢
    • 2010-09-11
    • 2010-10-19
    • 1970-01-01
    • 2012-08-24
    • 2019-04-15
    • 1970-01-01
    • 2010-12-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多