ConcurrentDictionary и потоки - PullRequest
0 голосов

У меня есть служба WCF в пуле приложений IIS. Метод службы WCF получает некоторые данные в JSON, например {"object": "someobject", "payload": [int key]}.
Для каждого запроса я запускаю новый поток для работы с ключом.
Ключ добавляет к ConcurrentDictionary и блокирует значение ConcurrentDictionary.
Цель этого заключается в том, что только один пример ключа может быть запущен один раз, например:
Резьба 1 - Запуск с клавишей 1
Резьба 2 - Запуск с клавишей 2
Тема 3 - Ожидание блокировка в теме 1
Но когда я удаляю ключ из ConcurrentDictionary, другой поток уже получает значение по ключу и работает с ним. Как этого избежать? Пример обработчика потока рядом с

static ConcurrentDictionary<string, object> taskDictionary=new ConcurrentDictionary<string, object>();
static void ThreadHandler(int key)
        {   
            try
            {
                var  lockElem=taskDictionary.GetOrAdd(key, new object());
//Thread 3 get value by key 1 here and waits here
                lock(lockElem)
                {
                    taskDictionary.TryRemove(key, out _);
// but Thread 1 removes value by key 1 here
//and Thread 4 can add value in Dictionary with same key (key 1)
                }
            }
            finally
            {
            }
        }

Проблема в этом случае: поток 1 использует GetOrAdd, затем блокирует значение, затем TryRemove. В это время поток 3. использует GetOrAdd, принимая значение, но ожидая блокировки от потока 1. Когда блокировка из потока 1 освобождает, поток 3 блокирует удаленное значение. В это время поток 4 использует GetOrAdd и создает новый элемент словаря (который не совпадает со значением, полученным потоком 3). И у нас есть 2 потока (поток 3 и поток 4), которые работают с одним и тем же ключом.

1 Ответ

0 голосов
/ 01 мая 2019

Функция TryRemove уже возвращает вам объект по умолчанию, если он еще не существует в словаре, поэтому вы можете просто сделать:

static ConcurrentDictionary<int, object> taskDictionary=new ConcurrentDictionary<int, object>();
static void ThreadHandler(int key)
{   
    try
    {
        object obj;

        // Remove object with key = 'key', or create a new object if it does not yet exist
        taskDictionary.TryRemove(key, out obj);

        // Do whatever you need to do with 'obj'

        // Add the object (back) to the dictionary if necessary
        taskDictionary.GetOrAdd(key, obj);
    }
    finally
    {
    }
}
...