C# 中的暂定锁?
Tentative locks in C#?
假设我想允许并行执行一些代码,但需要其他代码等待所有这些操作完成。
让我们想象一下 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 中是否有任何设计模式或 language/framework 功能可以以直接可靠的方式实现这一点,或者是否必须从头开始实现这一点?
我目前仅限于 .NET 3.5,我最感兴趣的是概念问题,而不是其他可能本身可以解决示例的集合。
在这种情况下,您可以使用 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. 您在评论中提到该值可能为空,因此 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();
}
假设我想允许并行执行一些代码,但需要其他代码等待所有这些操作完成。
让我们想象一下 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 中是否有任何设计模式或 language/framework 功能可以以直接可靠的方式实现这一点,或者是否必须从头开始实现这一点?
我目前仅限于 .NET 3.5,我最感兴趣的是概念问题,而不是其他可能本身可以解决示例的集合。
在这种情况下,您可以使用 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. 您在评论中提到该值可能为空,因此 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();
}