Работа с глобальным словарем внутри потоков - PullRequest
1 голос
/ 13 декабря 2011

Предположим, у меня есть Dictionary<string, string>.В моей консольной программе словарь объявлен как * 1002. *

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

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

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

Ответы [ 4 ]

7 голосов
/ 13 декабря 2011

Попробуйте использовать ConcurrentDictionary<TKey, TValue>, который предназначен для такого сценария.

Есть хорошее руководство здесь о том, как его использовать.

1 голос
/ 13 декабря 2011

Большой вопрос: вам нужен foreach, чтобы сделать снимок?

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

Однако, поскольку GetEnumerator не предоставляет снимок, он не будет перечислять то же начало в начале, что и в конце. Это может пропустить предметы или дублировать предметы. Вопрос в том, для тебя это катастрофа или нет.

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

Если вам действительно нужен жесткий снимок, используйте следующий подход.

Иметь ConcurrentDictionary (dict) и ReaderWriterLockSlim (rwls). При чтении и записи получите блокировку чтения (да, даже если вы пишете):

public static void AddToDict(string key, string value)
{
  rwls.EnterReadLock();
  try
  {
    dict[key] = value;
  }
  finally
  {
    rwls.ExitReadLock();
  }
}
public static bool ReadFromDict(string key, out string value)
{
  rwls.EnterReadLock();
  try
  {
    return dict.TryGetValue(key, out value);
  }
  finally
  {
    rwls.ExitReadLock();
  }
}

Теперь, когда мы хотим перечислить словарь, мы получаем блокировку записи (даже если мы читаем):

public IEnumerable<KeyValuePair<string, string>> EnumerateDict()
{
  rwls.EnterWriteLock();
  try
  {
    return dict.ToList();
  }
  finally
  {
    rwls.ExitWriteLock();
  }
}

Таким образом, мы получаем общую блокировку для чтения и записи, потому что ConcurrentDictionary имеет дело с конфликтами, связанными с этим для нас. Мы получаем эксклюзивную блокировку для перечисления, но достаточно долго, чтобы получить снимок словаря в списке, который затем используется только в этом потоке и не используется совместно с другими.

0 голосов
/ 13 декабря 2011

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

Решения:

  1. Требовать, чтобы все пользователи словаря установили блокировку мьютекса перед доступом к объекту, а затем сняли блокировку.
  2. Используйте класс ConcurrentDictionary в .NET 4.0.
0 голосов
/ 13 декабря 2011

С .NET 4 вы получите новый модный ConcurrentDictionary .Я думаю, что есть некоторые реализации на основе .NET 3.5.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...