Используя соединяют ключ Словаря <строка, объект>

[g1] Вы не можете сделать это самостоятельно, поскольку Java не допускает перегрузки операторов. [/g1] [g2] За исключением [g0] одного [/g0] исключения. + и + = перегружены для строковых объектов. [/g2]
8
задан Per Hornshøj-Schierbeck 1 October 2008 в 21:18
поделиться

6 ответов

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

Если Вы полностью инкапсулируете словарь и генерируете ключи сами (они никогда не передаются в, то можно быть в безопасности.

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

Вот почему Вы видите это:

public class Something
{
  private readonly object lockObj = new object();

  public SomethingReentrant()
  {
    lock(lockObj)    // Line A
    {
      // ...
     }
   }
}

вместо того, чтобы видеть выравнивают вышеупомянутый замененный

  lock(this)

Тем путем отдельный объект соединен, и видимость ограничена.

Отредактируйте Jon Skeet, правильно заметил, что lockObj выше должен быть только для чтения.

11
ответ дан 5 December 2019 в 06:24
поделиться

Примечание: Я принимаю исключение, когда изменение набора во время повторения уже фиксируется

Словарь не является ориентированным на многопотоковое исполнение набором, что означает, что не безопасно изменить и считать набор из различных потоков без внешней синхронизации. Хеш-таблица (был?) ориентированный на многопотоковое исполнение для one-writer-many-readers сценария, но Словаря имеет другую внутреннюю структуру данных и не наследовал эту гарантию.

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

  1. Вы блокируете ключ и начинаете изменять словарь
  2. Другой поток пытается получить значение для ключа, который, оказывается, попадает в тот же блок, как заблокировано один. Это - то, не только когда хэш-коды двух объектов являются тем же, но более часто когда hashcode%tableSize является тем же.
  3. Оба потока получают доступ к тому же блоку (связанный список ключей с тем же значением hashcode%tableSize)

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

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

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

3
ответ дан 5 December 2019 в 06:24
поделиться

Нет, это не работало бы.

Причиной является строковое интернирование. Это означает что:

string a = "Something";
string b = "Something";

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

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

class Something
{
    bool threadSafeBool = true;
    object threadSafeBoolLock = new object(); // Always lock this to use threadSafeBool
}

Я рекомендую сделать то же. Создайте Словарь с объектами блокирования для каждой ячейки матрицы. Затем заблокируйте эти объекты при необходимости.

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

9
ответ дан 5 December 2019 в 06:24
поделиться

Если я не ошибаюсь, исходное намерение состояло в том, чтобы соединить единственный элемент, вместо того, чтобы блокировать целый словарь (как блокировка уровня таблицы по сравнению с уровнем строки привязывают DB),

Вы не можете соединить ключ словаря как многих здесь объясненных.

То, что можно сделать, должно сохранить внутренний словарь объектов блокирования, который соответствует фактическому словарю. Таким образом, когда Вы хотели бы записать в YourDictionary[Key1], Вы сначала соедините InternalLocksDictionary[Key1] - поэтому только единственный поток запишет в YourDictionary.

(не слишком чистый) пример может быть найден здесь.

2
ответ дан 5 December 2019 в 06:24
поделиться

В Вашем примере Вы не можете сделать то, что Вы хотите сделать!

Вы получите Систему. InvalidOperationException с сообщением Набора был изменен; операция перечисления не может выполниться.

Вот пример для доказательства:

using System.Collections.Generic;
using System;

public class Test
{
    private Int32 age = 42;

    static public void Main()
    {
       (new Test()).TestMethod();
    }

    public void TestMethod()
    {
        Dictionary<Int32, string> myDict = new Dictionary<Int32, string>();

        myDict[age] = age.ToString();

        foreach(KeyValuePair<Int32, string> pair in myDict)
        {
            Console.WriteLine("{0} : {1}", pair.Key, pair.Value);
            ++age;
            Console.WriteLine("{0} : {1}", pair.Key, pair.Value);
            myDict[pair.Key] = "new";
            Console.WriteLine("Changed!");
        }
    }   
}

Вывод был бы:

42 : 42
42 : 42

Unhandled Exception: System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
   at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
   at System.Collections.Generic.Dictionary`2.Enumerator.MoveNext()
   at Test.TestMethod()
   at Test.Main()
0
ответ дан 5 December 2019 в 06:24
поделиться

Я вижу несколько потенциальных проблем там:

  1. строки могут быть совместно использованы, таким образом, Вы не обязательно знаете, кто еще мог бы соединять тот ключевой объект для какой другая причина
  2. строки не могли бы быть совместно использованы: можно соединять один строковый ключ со значением "Key1", и некоторая другая часть кода может иметь другой строковый объект, который также содержит символы "Key1". К словарю они - тот же ключ, но насколько блокировка обеспокоена, что они - различные объекты.
  3. Та блокировка не предотвратит изменения в самих объектах значения, т.е. matrixElements[someKey].ChangeAllYourContents()
0
ответ дан 5 December 2019 в 06:24
поделиться
Другие вопросы по тегам:

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