Here on stack overflow I've found the code that memoizes single-argument functions:
static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
var d = new Dictionary<A, R>();
return a=>
{
R r;
if (!d.TryGetValue(a, out r))
{
r = f(a);
d.Add(a, r);
}
return r;
};
}
While this code does its job for me, it fails sometimes when the memoized function is called from the multiple threads simultaneously: the Add
method gets called twice with the same argument and throws an exception.
How can I make the memoization thread-safe?
You can use ConcurrentDictionary.GetOrAdd
which does everything you need:
static Func<A, R> ThreadsafeMemoize<A, R>(this Func<A, R> f)
{
var cache = new ConcurrentDictionary<A, R>();
return argument => cache.GetOrAdd(argument, f);
}
The function f
should be thread-safe itself, because it can be called from multiple threads simultaneously.
This code also doesn't guarantee that function f
is called only once per unique argument value. It can be called many times, in fact, in the busy environment. If you need this kind of contract, you should take a look at the answers in this related question, but be warned that they're not as compact and require using locks.