Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/277.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 线程安全记忆_C#_Multithreading_Locking_Thread Safety_Memoization - Fatal编程技术网

C# 线程安全记忆

C# 线程安全记忆,c#,multithreading,locking,thread-safety,memoization,C#,Multithreading,Locking,Thread Safety,Memoization,让我们以函数记忆的方法为起点: 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 =

让我们以函数记忆的方法为起点:

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;
    };
}
这显然是一个可怕的想法,因为它阻止我们同时对许多不同的参数计算
f1
。如果
a
具有值类型,则锁定
a
将不起作用(无论如何,这是个坏主意,因为我们不控制
a
,外部代码也可能锁定它)

以下是我能想到的两个选择:

假设一个
Lazy
类用于延迟计算(请参阅):

public static funct Memoize(此函数)
{
var map=newdictionary();
返回a=>
{
懒惰的结果;
锁(地图)
{
如果(!map.TryGetValue(a,输出结果))
{  
结果=()=>f(a);
添加(a,结果);
}
}
返回结果值;
};
}
或保留其他对象字典以进行同步:

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;
      }
    };
}
public static funct Memoize(此函数)
{
var map=newdictionary();
var mapSync=新字典();
返回a=>
{
R值;
对象同步;
锁定(映射同步)
{ 
如果(!mapSync.TryGetValue(a,不同步))
{ 
sync=新对象();
mapSync[a]=同步;
}
}
锁(地图)
{
if(映射TryGetValue(a,输出值))
返回值;
}
锁定(同步)
{
数值=f(a);
锁(地图)
{
map[a]=值;
}
返回值;
}
};
}

有更好的选择吗?

没有,它们不是更好的选择

具有延迟计算的版本是毫无意义的,因为您无论如何都会立即对其进行计算。带有同步字典的版本无法正常工作,因为在使用地图字典之前,您没有在锁中保护地图字典

你称之为恐怖的版本实际上是最好的选择。您必须在锁中保护地图字典,以便一次只有一个线程可以访问它。字典不是线程安全的,因此,如果让一个线程读取字典,而另一个线程正在更改字典,则会出现问题


记住,在map对象上使用lock并不能保护map对象本身,它只是使用map引用作为标识符,一次保留多个线程来运行锁中的代码。您必须将访问对象的所有代码放入锁中,而不仅仅是更改对象的代码。

如果您已经有了
惰性类型,我假设您使用的是.net 4.0,因此您还可以使用:

public static funct Memoize(此函数)
{
var map=新的ConcurrentDictionary();
返回a=>
{
Lazy Lazy=新的Lazy(()=>f(a),LazyExecutionMode.EnsureSingleThreadSafeExecution);
if(!map.TryAdd(a,lazy))
{
返回映射[a]。值;
}
返回lazy.Value;
};
}
您是否阅读了文章中与线程安全相关的内容

使Memoize线程安全的最简单方法可能是在地图上加一个锁

这将确保正在记忆的函数对于每一组不同的参数只运行一次

在我的Roboral游戏示例中,我实际上使用函数记忆来充当“代理单例”。它不是一个真正的单例,因为每个工厂实例可以有一个实例(除非工厂是静态的)。但这正是我想要的


您不希望计算同一个值两次,并且希望多个线程能够同时计算值和/或检索值。为此,您需要使用某种条件变量和细粒度锁定系统

想法是这样的。当不存在值时,您将一个值放入同步映射,然后需要该值的任何线程都将等待该值,否则您将只获取当前值。通过这种方式,映射的锁定最小化为查询值和返回值

    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 = null;
            bool calc = false;
            bool wait = false;
            lock (map)
            {
                if (!map.TryGetValue(a, out value))
                {
                    //its not in the map
                    if (!mapSync.TryGetValue(a, out sync))
                    {
                        //not currently being created
                        sync = new object();
                        mapSync[a] = sync;
                        calc = true;

                    }
                    else
                    {
                        calc = false;
                        wait = true;
                    }
                }
            }
            if(calc)
            {
                lock (sync)
                {
                    value = f(a);
                    lock (map)
                    {
                        map.Add(a, value);
                        mapSync.Remove(a);
                    }
                    Monitor.PulseAll(sync);
                    return value;
                }
            }
            else if (wait)
            {
                lock (sync)
                {
                    while (!map.TryGetValue(a, out value))
                    {
                        Monitor.Wait(sync);
                    }
                    return value;
                }
            }

            lock (map)
            {
                return map[a];
            }

        };
    }
public static funct Memoize(此函数)
{
var map=newdictionary();
var mapSync=新字典();
返回a=>
{
R值;
对象同步=空;
布尔计算=假;
bool wait=false;
锁(地图)
{
如果(!map.TryGetValue(a,输出值))
{
//它不在地图上
如果(!mapSync.TryGetValue(a,不同步))
{
//当前未创建
sync=新对象();
mapSync[a]=同步;
计算=真;
}
其他的
{
calc=假;
等待=真;
}
}
}
如果(计算)
{
锁定(同步)
{
数值=f(a);
锁(地图)
{
添加(a,值);
mapSync.Remove(a);
}
监控脉冲(同步);
返回值;
}
}
否则,如果(等待)
{
锁定(同步)
{
而(!map.TryGetValue(a,输出值))
{
监视器。等待(同步);
}
返回值;
}
}
锁(地图)
{
返回图[a];
}
};
}

这只是一个快速的第一次尝试,但我认为它展示了这项技术。在这里,您是在用额外的内存换取速度。

由于惰性构造函数的enum参数,Thomas的答案似乎无法在.NET 4.0下编译。我在下面修改了它。我还添加了一个可选参数,用于提供自己的相等比较器。如果TInput没有实现,这是有用的
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;
      }
    };
}
public static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
  var map = new ConcurrentDictionary<A, Lazy<R>>();
  return a =>
    {
      Lazy<R> lazy = new Lazy<R>(() => f(a), LazyExecutionMode.EnsureSingleThreadSafeExecution);
      if(!map.TryAdd(a, lazy))
      {
        return map[a].Value;
      }
      return lazy.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 = null;
            bool calc = false;
            bool wait = false;
            lock (map)
            {
                if (!map.TryGetValue(a, out value))
                {
                    //its not in the map
                    if (!mapSync.TryGetValue(a, out sync))
                    {
                        //not currently being created
                        sync = new object();
                        mapSync[a] = sync;
                        calc = true;

                    }
                    else
                    {
                        calc = false;
                        wait = true;
                    }
                }
            }
            if(calc)
            {
                lock (sync)
                {
                    value = f(a);
                    lock (map)
                    {
                        map.Add(a, value);
                        mapSync.Remove(a);
                    }
                    Monitor.PulseAll(sync);
                    return value;
                }
            }
            else if (wait)
            {
                lock (sync)
                {
                    while (!map.TryGetValue(a, out value))
                    {
                        Monitor.Wait(sync);
                    }
                    return value;
                }
            }

            lock (map)
            {
                return map[a];
            }

        };
    }
    public static Func<TInput, TResult> Memoize<TInput, TResult>(
        this Func<TInput, TResult> func, IEqualityComparer<TInput> comparer = null)
    {
        var map = comparer == null
                      ? new ConcurrentDictionary<TInput, Lazy<TResult>>()
                      : new ConcurrentDictionary<TInput, Lazy<TResult>>(comparer);

        return input =>
               {
                   var lazy = new Lazy<TResult>(() => func(input), LazyThreadSafetyMode.ExecutionAndPublication);

                   return map.TryAdd(input, lazy)
                              ? lazy.Value
                              : map[input].Value;
               };
    }
    public void TestMemoize()
    {
        Func<int, string> mainFunc = i =>
                                     {
                                         Console.WriteLine("Evaluating " + i);
                                         Thread.Sleep(1000);
                                         return i.ToString();
                                     };

        var memoized = mainFunc.Memoize();

        Parallel.ForEach(
            Enumerable.Range(0, 10),
            i => Parallel.ForEach(Enumerable.Range(0, 10), j => Console.WriteLine(memoized(i))));
    }
public static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
    var cache = new ConcurrentDictionary<A, R>();
    return a => cache.GetOrAdd(a, f);
};
public static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
    var cache = new ConcurrentDictionary<A, Lazy<R>>();
    return a => cache.GetOrAdd(a, new Lazy<R>(() => f(a))).Value;
}
public static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
    var cache = new ConcurrentDictionary<A, R>();
    var syncMap = new ConcurrentDictionary<A, object>();
    return a =>
    {
        R r;
        if (!cache.TryGetValue(a, out r))
        {
            var sync = syncMap.GetOrAdd(a, new object());
            lock (sync)
            {
                r = cache.GetOrAdd(a, f);
            }
            syncMap.TryRemove(a, out sync);
        }
        return r;
    };
}
public class SynchronizedConcurrentDictionary<TKey, TValue> : ConcurrentDictionary<TKey, TValue>
{
    private readonly ReaderWriterLockSlim _cacheLock = new ReaderWriterLockSlim();

    public new TValue GetOrAdd(TKey key, Func<TKey, TValue> valueFactory)
    {
        TValue result;

        _cacheLock.EnterWriteLock();
        try
        {
            result = base.GetOrAdd(key, valueFactory);
        }
        finally
        {
            _cacheLock.ExitWriteLock();
        }

        return result;
    }
}
public static Func<A, R> Memoize<A, R>(this Func<A, R> f)
{
    var cache = new SynchronizedConcurrentDictionary<A, R>();

    return key => cache.GetOrAdd(key, f);
}