Ориентированный на многопотоковое исполнение memoization

Нет двоичного спецификатора преобразования в glibc обычно.

возможно добавить пользовательские типы преобразования к printf () семья функций в glibc. См. register_printf_function для деталей. Вы могли добавить пользовательское %b преобразование для своего собственного использования, если оно упрощает код приложения для имения его в наличии.

Вот пример из как реализовать пользовательский printf форматы в glibc.

23
задан Alexey Romanov 10 August 2009 в 14:46
поделиться

4 ответа

Если у вас уже есть этот тип Lazy , я полагаю, вы используете .net 4.0, поэтому вы также можете использовать ConcurrentDictionary :

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;
    };
}
10
ответ дан 29 November 2019 в 01:25
поделиться

Нет, это не лучшие варианты.

Версия с отложенной оценкой бессмысленна, так как вы все равно оцениваете ее немедленно. Версия со словарем синхронизации не работает должным образом, поскольку вы не защищаете словарь карты внутри блокировки перед ее использованием.

Версия, которую вы назвали ужасной, на самом деле является лучшим вариантом. Вы должны защитить словарь карты внутри блокировки, чтобы к нему мог получить доступ только один поток. Словарь не является потокобезопасным, поэтому, если вы позволите одному потоку читать из него, в то время как другой поток его изменяет, у вас возникнут проблемы.

Помните, что использование блокировки на объекте карты не защищает сам объект карты, он использует только ссылку на карту в качестве идентификатора, чтобы сохранить более одного потока одновременно для запуска кода внутри блокировки. Вы должны поместить в блокировку весь код, который обращается к объекту, а не только код, изменяющий объект.

1
ответ дан 29 November 2019 в 01:25
поделиться

Вы не хотите, чтобы одно и то же значение вычислялось дважды, и вы хотите, чтобы многие потоки могли вычислять значения и / или получать значения одновременно. Для этого вам нужно будет использовать какую-то переменную условия и детализированную систему блокировки.

Вот идея. когда значение отсутствует, вы помещаете значение в карту синхронизации, а затем любой поток, которому требуется это значение, будет ждать его, иначе вы просто получите текущее значение. таким образом блокировка карты сводится к минимуму запроса значений и возврата значений.

    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];
            }

        };
    }

Это всего лишь первая быстрая попытка, но я думаю, что она демонстрирует методику. Здесь вы жертвуете дополнительной памятью на скорость.

1
ответ дан 29 November 2019 в 01:25
поделиться

Вы читали комментарий Дайера , связанный с безопасностью потоков в статье ?

Вероятно, самый простой способ сделать Memoize поточно-ориентированным - это установить блокировку на карту.

Это гарантирует, что мемоизируемая функция будет запускаться только один раз для каждого набора различных аргументов.

В моем примере игры RoboRally я фактически использовал мемоизацию функций, чтобы действовать как «суррогатный синглтон». Это н' На самом деле это синглтон, поскольку на каждый экземпляр фабрики может быть один экземпляр (если только фабрика не статическая). Но это именно то, что я хотел.

0
ответ дан 29 November 2019 в 01:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: