Что лучший способ реализовать ориентированный на многопотоковое исполнение Словарь?

Я помещаю превосходный ответ JLBorges на аналогичный вопрос дословно из cplusplus.com, так как это наиболее краткое объяснение, которое я прочитал по этому вопросу.

] В шаблоне, который мы пишем, есть два типа имен, которые можно использовать - зависимые имена и не зависимые имена. Зависимое имя - это имя, которое зависит от параметра шаблона; неизменяемое имя имеет то же значение, независимо от параметров шаблона.

Например:

template< typename T > void foo( T& x, std::string str, int count )
{
    // these names are looked up during the second phase
    // when foo is instantiated and the type T is known
    x.size(); // dependant name (non-type)
    T::instance_count ; // dependant name (non-type)
    typename T::iterator i ; // dependant name (type)

    // during the first phase, 
    // T::instance_count is treated as a non-type (this is the default)
    // the typename keyword specifies that T::iterator is to be treated as a type.

    // these names are looked up during the first phase
    std::string::size_type s ; // non-dependant name (type)
    std::string::npos ; // non-dependant name (non-type)
    str.empty() ; // non-dependant name (non-type)
    count ; // non-dependant name (non-type)
}

То, что зависит от зависимого имени, может быть чем-то другим для каждого конкретного экземпляра шаблона. Как следствие, шаблоны C ++ подвержены «двухфазному поиску имен». Когда шаблон сначала анализируется (до того, как выполняется какое-либо создание), компилятор просматривает не зависящие имена. Когда происходит конкретное создание шаблона, параметры шаблона известны к тому времени, и компилятор ищет зависимые имена.

На первом этапе анализатор должен знать, является ли зависимое имя именем типа или имени не-типа. По умолчанию зависимым именем считается имя не-типа.

Использовать ключевое слово typename только в объявлениях шаблонов и определениях, приведенных ниже.

blockquote>

у вас есть квалифицированное имя, которое относится к типу и зависит от параметра шаблона.

108
задан Slim 16 November 2012 в 15:30
поделиться

6 ответов

Как Peter сказал, можно инкапсулировать всю потокобезопасность в классе. Необходимо будет быть осторожными с любыми событиями, которые Вы представляете или добавляете, удостоверяясь, что они вызываются за пределами любых блокировок.

public class SafeDictionary<TKey, TValue>: IDictionary<TKey, TValue>
{
    private readonly object syncRoot = new object();
    private Dictionary<TKey, TValue> d = new Dictionary<TKey, TValue>();

    public void Add(TKey key, TValue value)
    {
        lock (syncRoot)
        {
            d.Add(key, value);
        }
        OnItemAdded(EventArgs.Empty);
    }

    public event EventHandler ItemAdded;

    protected virtual void OnItemAdded(EventArgs e)
    {
        EventHandler handler = ItemAdded;
        if (handler != null)
            handler(this, e);
    }

    // more IDictionary members...
}

Редактирование: документы MSDN указывают, что перечисление по сути не ориентировано на многопотоковое исполнение. Это может быть одной причиной представления объекта синхронизации вне Вашего класса. Другой способ приблизиться, который должен был бы предоставить некоторые методы для выполнения действия со всеми участниками и блокировкой вокруг перечисления участников. Проблема с этим состоит в том, что Вы не знаете, передало ли действие этому вызовы функции некоторый член Вашего словаря (который привел бы к мертвой блокировке). Представление объекта синхронизации позволяет потребителю принимать те решения и не скрывает мертвую блокировку в Вашем классе.

43
ответ дан fryguybob 24 November 2019 в 03:26
поделиться

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

public void Add(TKey key, TValue value)
{
    lock (this.syncRoot)
    {
        this.innerDictionary.Add(key, value);
    }
}

public bool ContainsKey(TKey key)
{
    lock (this.syncRoot)
    {
        return this.innerDictionary.ContainsKey(key);
    }
}

Тогда, что происходит, когда Вы называете этот, предположительно, ориентированный на многопотоковое исполнение бит кода от нескольких потоков? Это будет всегда работать хорошо?

if (!mySafeDictionary.ContainsKey(someKey))
{
    mySafeDictionary.Add(someKey, someValue);
}

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

, Что означает писать этот тип сценария правильно, Вам нужна блокировка внешний словарь, например,

lock (mySafeDictionary)
{
    if (!mySafeDictionary.ContainsKey(someKey))
    {
        mySafeDictionary.Add(someKey, someValue);
    }
}

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

  1. Использование нормальное Dictionary<TKey, TValue> и синхронизируется внешне, включая составные операции на нем, или

  2. Запись новая ориентированная на многопотоковое исполнение обертка с различным интерфейсом (т.е. не IDictionary<T>), который комбинирует операции такой как AddIfNotContained метод, таким образом, Вы никогда не должны комбинировать операции от него.

(я склонен идти с № 1 сам)

59
ответ дан Greg Beech 24 November 2019 в 03:26
поделиться

Вы не должны публиковать свой частный объект блокирования через свойство. Объект блокирования должен существовать конфиденциально для единственной цели действовать как точка рандеву.

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

6
ответ дан Jonathan Webb 24 November 2019 в 03:26
поделиться

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

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

Лично, я нашел, что лучший способ реализовать ориентированный на многопотоковое исполнение класс через неизменность. Это действительно сокращает количество проблем, с которыми можно столкнуться с потокобезопасностью. Выезд Блог Eric Lippert для получения дополнительной информации.

5
ответ дан JaredPar 24 November 2019 в 03:26
поделиться

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

Для Разработки: , Что заканчивает тем, что произошло, то, что Ваш словарь заблокирован в течение более длительного промежутка времени, чем необходимо.

то, Что происходит в Вашем случае, следующее:

Говорят, что поток A получает блокировку на SyncRoot прежде вызов к m_mySharedDictionary. Добавить. Распараллельте B, тогда пытается получить блокировку, но заблокирован. На самом деле все другие потоки заблокированы. Потоку A позволяют звонить в Добавить метод. В операторе блокировки в рамках Добавить метода A потока позволяют получить блокировку снова, потому что это уже владеет им. После выхода из контекста блокировки в рамках метода и затем вне метода, A потока выпустил все блокировки, позволяющие другие потоки продолжаться.

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

3
ответ дан El Ronnoco 24 November 2019 в 03:26
поделиться
-5
ответ дан MagicKat 24 November 2019 в 03:26
поделиться
Другие вопросы по тегам:

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