В ConcurrentDictionary операция чтения считывает последнее обновленное значение?

Я использую ConcurrentDictionary (ongoingConnectionDic) в своем коде:

  1. Я проверяю, существует ли номер последовательного порта в словаре.
  2. Если не существует, я добавляю его в словарь.
  3. Я выполняю связь с последовательным портом.
  4. Я удаляю элемент из ongoingConnectionDic.
  5. Если существует, я помещаю поток в ожидание.

Мой вопрос: могу ли я гарантировать, что при выполнении операции чтения никакой другой поток одновременно не записывает/не обновляет значение? Итак, читаю ли я самое последнее значение словаря?

Если нет, то как мне достичь того, чего я хочу?

Пример программы:

    class Program
{
    // Dictionary in question
    private static ConcurrentDictionary<string, string> ongoingPrinterJobs = 
        new ConcurrentDictionary<string, string>();

    private static void sendPrint(string printerName)
    {
        if (ongoingPrinterJobs.ContainsKey(printerName))
        {
            // Add to pending list and run a thread to finish pending jobs by calling print();


        }
        else
        {
            ongoingPrinterJobs.TryAdd(printerName, ""); // -- Add it to the dictionary so that no other thread can
                                                        // use the printer

            ThreadPool.QueueUserWorkItem(new WaitCallback(print), printerName);
        }

    }

    private static void print(object stateInfo)
    {
        string printerName = (stateInfo as string);
        string dummy;
        // do printing work

        // Remove from dictionary
        ongoingPrinterJobs.TryRemove(printerName, out dummy);
    }


    static void Main(string[] args)
    {

        // Run threads here in random to print something on different printers
        // Sample run with 10 printers
        Random r = new Random();
        for ( int i = 0 ; i < 10 ; i++ )
        {
            sendPrint(r.Next(0, 10).ToString());
        }



    }

person bkj    schedule 21.02.2016    source источник
comment
Затем вам нужно пройти курс lock по каждому методу собственной реализации IDictionary<K, V>.   -  person Enigmativity    schedule 22.02.2016
comment
Даже делая это, вы просто выталкиваете свою проблему за пределы словаря. Вы просто не можете знать, что другой поток ждет за блокировкой, чтобы обновить значение. Таким образом, последнее значение может быть уже вычислено, но не обновлено в словаре.   -  person Enigmativity    schedule 22.02.2016
comment
Не могли бы вы опубликовать свой код, а не расплывчатые пять шагов, которые вы разместили в вопросе?   -  person Enigmativity    schedule 22.02.2016
comment
Я добавил код выше. Пожалуйста, предложите. Спасибо.   -  person bkj    schedule 22.02.2016
comment
Вы получаете то, что было написано последним. Если поток обновляет словарную запись на наносекунду позже, то то, что вы прочитали, уже не будет последним. ConcurrentDictionary является потокобезопасным, что автоматически не делает его безопасным для потоков.   -  person Hans Passant    schedule 22.02.2016
comment
Вам действительно нужно опубликовать весь свой код - вам нужен минимально воспроизводимый пример - потому что прямо сейчас я вижу часть что вы делаете, и это выглядит довольно хрупким. Я хотел бы иметь возможность копировать и вставлять ваш код в консольное приложение или LINQPad и нажимать "Выполнить", чтобы он хотя бы скомпилировался. Это возможно?   -  person Enigmativity    schedule 22.02.2016
comment
Как говорит Ганс, все, что обещает ConcurrentDictionary, это то, что вы можете использовать его одновременно, не повреждая объект. Это необязательно означает, что ваше использование будет потокобезопасным. И на самом деле, в небольшом фрагменте кода, который вы разместили здесь, это не выглядит безопасным: если предположить, что несколько потоков, то, если один из них только что проверил ContainsKey(), он может быть вытеснен, а затем другой поток может выполнить ту же проверку, а затем написать к словарю. Теперь первый поток считает, что ключ не существует, и сделает что-то не так.   -  person Peter Duniho    schedule 22.02.2016
comment
Однако большая проблема с вопросом заключается в том, что, хотя ясно, что опубликованный код небезопасен, на самом деле неясно, чего вы на самом деле пытаетесь достичь. Хороший ответ поможет вам решить вашу проблему, а не просто скажет вам, что вы этого не сделали. Но без лучшего вопроса сделать это невозможно. Пожалуйста, предоставьте хороший минимально воспроизводимый пример, который четко показывает, что вы делаете, а также подробное объяснение того, что код должен делать и, если отличается, что он делает сейчас.   -  person Peter Duniho    schedule 22.02.2016
comment
Я только что добавил тестируемый/выполняемый пример. Спасибо за все комментарии/предложения.   -  person bkj    schedule 22.02.2016
comment
Любые предложения после того, как я предоставил тестируемый пример?   -  person bkj    schedule 22.02.2016


Ответы (1)


Параллельные коллекции делают «моментальный снимок» коллекции при перечислении. Это делается для того, чтобы перечислитель не стал недействительным, если появится другой поток и запишет в коллекцию.

Такой метод, как ContainsKey, может перечислять элементы в словаре (вам придется посмотреть на реализацию), и в этом случае вы можете читать устаревшие данные.

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

С учетом сказанного, как отмечали другие в своих комментариях, необходимо учитывать и другие вопросы безопасности потоков (например, условия гонки).

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

person fourpastmidnight    schedule 22.02.2016