In Situationen wie diesem können Sie eine ReaderWriterLockSlim verwenden, es wird mehrere Leser erlauben, bis jemand schreiben will, es blockiert dann alle Leser und erlaubt nur einen einzigen Schreiber durch.
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();
}
}
}
Sie können jedoch überprüfen wollen, um zu sehen, während Sie in dem Warte die Schreibsperre, jemanden zu nehmen sonst möglicherweise den Datensatz in in den Cache eingegeben, in diesem Fall, dass Sie ein EnterUpgradeableReadLock()
verwenden können, ermöglicht diese unbegrenzte Menschen innerhalb von EnterReadLock()
zu sein, aber nur eine einzelne Person kann im Upgrade-Schloss sein (und es wird immer noch keine Schreibsperren geben). Das Upgrade-fähige Schloss ist nützlich, wenn Sie wissen, dass Sie wahrscheinlich schreiben werden, aber es gibt eine Möglichkeit, nicht zu schreiben.
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. Sie haben in einem Kommentar erwähnt, dass der Wert null sein könnte, also würde FirstOrDefault()
nicht funktionieren. Verwenden Sie in diesem Fall eine Erweiterungsmethode, um eine TryFirst()
Funktion zu erstellen.
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();
}
Klingt wie Sie brauchen [ReaderWriterLockSlim] (https://msdn.microsoft.com/en-us/library/system.threading.readerwriterlockslim (v = vs.110) aspx), die beginnen kann eine Lesersperre, und zu einer Schreibsperre aufgerüstet werden. –
Ihre Suche ist etwas ineffizient, Sie aufzählen zweimal, könnten Sie 'var result = Collection.FirstOrDefault (kvp => kvp.Key == Schlüssel); if (Ergebnis! = Standard (KeyValuePair)) {return result.Value; } 'und mache die Suche nur einmal. –
@MatthewWatson Danke, das sieht sehr gut aus, ich werde damit herumspielen. – Alex