のが出発点として、メモ化を機能させるWes Dyer'sアプローチを見てみましょう:スレッドセーフなメモ化
public static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
var map = new Dictionary<A, R>();
return a =>
{
R value;
if (map.TryGetValue(a, out value))
return value;
value = f(a);
map.Add(a, value);
return value;
};
}
問題であり、複数のスレッドからそれを使用した場合、我々はトラブルに取得することができます。
Func<int, int> f = ...
var f1 = f.Memoize();
...
in thread 1:
var y1 = f1(1);
in thread 2:
var y2 = f1(1);
// We may be recalculating f(1) here!
これを回避しようとしましょう。 map
にロック:それは一度に多くの異なる引数にf1
を計算するから私たちを防ぐため
public static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
var map = new Dictionary<A, R>();
return a =>
{
R value;
lock(map)
{
if (map.TryGetValue(a, out value))
return value;
value = f(a);
map.Add(a, value);
}
return value;
};
}
することは、明らかに恐ろしい考えです。 a
のロックは、a
に値の型がある場合には機能しません(そして、どんな場合でも、私たちはa
を制御せず、外部コードもロックされる可能性があるため)。
(hereを参照)遅延評価のためのLazy<T>
クラスを仮定:
public static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
var map = new Dictionary<A, Lazy<R>>();
return a =>
{
Lazy<R> result;
lock(map)
{
if (!map.TryGetValue(a, out result))
{
result =() => f(a);
map.Add(a, result);
}
}
return result.Value;
};
}
や同期のためのオブジェクトの追加辞書を保つ:
public static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
var map = new Dictionary<A, R>();
var mapSync = new Dictionary<A, object>();
return a =>
{
R value;
object sync;
lock(mapSync)
{
if (!mapSync.TryGetValue(a, out sync))
{
sync = new object();
mapSync[a] = sync;
}
}
lock(map)
{
if (map.TryGetValue(a, out value))
return value;
}
lock(sync)
{
value = f(a);
lock(map)
{
map[a] = value;
}
return value;
}
};
}
ここ
は、私は考えることができる2つのオプションがあります
もっと良いオプションはありますか?
私はこれが優れた答えだと言いたいと思います。ありがとうございました! –