C#中的暂定锁?

时间:2016-06-10 15:15:31

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

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

除了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,我对概念问题最感兴趣,而不是其他可能解决这个问题的集合。

1 个答案:

答案 0 :(得分:2)

在这种情况下你可以使用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();
}