none
Потокобезопасность. Организация наилучшей записи/чтения данных. RRS feed

  • Вопрос

  • Здравствуйте!
    Хотелось бы найти оптимальный способ для потокобезопасности обращения к данным массива.
    Насколько я понимаю - чтение данных из массива можно выполнять сразу из нескольких потоков, а вот запись только из одного. Каким образом можно построить блокировку, чтобы при чтении не блокировать остальные потоки на чтение?

    Привожу код

    class Test
    { object lockObj = new object(); Dictionary<someT, someValue> m_dictionary = new Dictionary<someT, someValue>(); public someValue GetValue(someT) { lock (lockObj) {
    // Поиск значения в m_dictionary ... } } public void AddValue(someT, someValue) { lock(lockObj) { // Добавление значения в m_dictionary ...
    } }
    }
    Предположим, что объект класса Test будут использовать сразу несколько потоков.
    В коде видно, что при попытке одновременного чтения из разных потоков - по сути чтение сможет выполнять только один поток, т.к. остальные будут заблокированы, даже в отсутствии операции на запись.
    Как построить блокировку, чтобы не блокировать одновременное чтение из разных потоков в отсутствии записи, но в тоже время сохранить потокобезопасность?
    • Перемещено Siddharth Chavan 1 октября 2010 г. 22:43 MSDN Forums Consolidation (От:Visual C#)
    11 января 2010 г. 8:15

Ответы

  • Извините, но меня не устраивают ответы - "может сработать, а может и нет"..

    Именно поэтому я не пометил галочку - Предложить как ответ.

    А теперь помечу
    http://msdn.microsoft.com/ru-ru/library/system.threading.readerwriterlock.aspx
    • Предложено в качестве ответа OlegGel 11 января 2010 г. 10:37
    • Помечено в качестве ответа G V 11 января 2010 г. 10:52
    11 января 2010 г. 10:36

Все ответы

  • class Test
    {
        object lockObj = new object();
        Dictionary<someT, someValue> m_dictionary = new Dictionary<someT, someValue>();
    
        public someValue GetValue(someT)
        {
            // Поиск значения в m_dictionary
            ...
        }
        public void AddValue(someT, someValue)
        {
            lock(lockObj)
            {
                // Добавление значения в m_dictionary
                ...
            }
        }
    }
    

    11 января 2010 г. 8:28
  • А разве не нарушается потокобезопасность?
    Предположим, что 10 потоков выполняют чтение из массива и в этот же момент происходит добавление нового элемента в массив.. Думаю, что скорее всего студия выбросит исключение..

    11 января 2010 г. 8:34
  • А разве не нарушается потокобезопасность?
    Предположим, что 10 потоков выполняют чтение из массива и в этот же момент происходит добавление нового элемента в массив.. Думаю, что скорее всего студия выбросит исключение..

    Почему?:)
    На операции записи у вас стоит блокировка, так что в момент записи доступ к коллеции имеет только один поток, тот что записывает. А чтение не влияет на коллекцию, разве что вы неверные данные можете прочесть если не блокируете операцию чтения. Например, поток начал чтение, приостановился, в этот элемент записалось новое значение, и вы продолжили чтение.
    11 января 2010 г. 8:44
  • А разве не нарушается потокобезопасность?
    Предположим, что 10 потоков выполняют чтение из массива и в этот же момент происходит добавление нового элемента в массив.. Думаю, что скорее всего студия выбросит исключение..

    Почему?:)
    На операции записи у вас стоит блокировка, так что в момент записи доступ к коллеции имеет только один поток, тот что записывает. А чтение не влияет на коллекцию, разве что вы неверные данные можете прочесть если не блокируете операцию чтения. Например, поток начал чтение, приостановился, в этот элемент записалось новое значение, и вы продолжили чтение.

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

    Пусть в GetValue(someT) поочередно перебираются все элементы этой коллекции.. (например это занимает 10 секунд, коллекция очень большая)

    Есть несколько потоков и в один прекрасный момент времени случится одновременный вызов, например:
    Thread1->GetValue()
    Thread2->GetValue()
    Thread3->AddValue()
    ..

    Если не блокировать операцию чтения, то получится, что во время выполнения GetValue() от Thread1 или Thread2 произойдет изменения коллекции и выпадет исключение. Я не прав?

    11 января 2010 г. 8:53
  • Да, только совсем необязательно исключение может выдать. Может всё сработает гладко, только с испорченными результатами.
    11 января 2010 г. 8:58
  • Извините, но меня не устраивают ответы - "может сработать, а может и нет"..

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

    Т.е. случаи:
    - нет записи -> потоки, выполняющие чтение, свободно обращаются к массиву
    - нужна запись -> чтение блокируется, выполняется запись, чтение разблокируется и опять осуществляется свободный доступ всех потоков на чтение, до следующей записи
    11 января 2010 г. 10:04
  • Извините, но меня не устраивают ответы - "может сработать, а может и нет"..

    Именно поэтому я не пометил галочку - Предложить как ответ.

    А теперь помечу
    http://msdn.microsoft.com/ru-ru/library/system.threading.readerwriterlock.aspx
    • Предложено в качестве ответа OlegGel 11 января 2010 г. 10:37
    • Помечено в качестве ответа G V 11 января 2010 г. 10:52
    11 января 2010 г. 10:36
  • А мож вот так?)

    class Test
        {
            object lockObj = new object();
            Dictionary<someT, someValue> m_dictionary = new Dictionary<someT, someValue>();
    
            public someValue GetValue(someT)
            {
                lock (lockObj)
                {
                }
                // Поиск значения в m_dictionary
                ...
            }
            public void AddValue(someT, someValue)
            {
                lock(lockObj)
                {
                    // Добавление значения в m_dictionary
                    ...
                }
            }
        }
    

    Хотя через ReaderWriterLock будет правильней :)

    11 января 2010 г. 10:43
  • А мож вот так?)

    class Test
    
        {
    
            object lockObj = new object();
    
            Dictionary<someT, someValue> m_dictionary = new Dictionary<someT, someValue>();
    
    
    
            public someValue GetValue(someT)
    
            {
    
                lock (lockObj)
    
                {
    
                }
    
                // Поиск значения в m_dictionary
    
                ...
    
            }
    
            public void AddValue(someT, someValue)
    
            {
    
                lock(lockObj)
    
                {
    
                    // Добавление значения в m_dictionary
    
                    ...
    
                }
    
            }
    
        }
    
    
    
    

    Хотя через ReaderWriterLock будет правильней :)


    Нет, это некоректно, т.к. если обратиться к методу AddValue в момент времени выполнения блока в методе  GetValue "// Поиск значения в m_dictionary", то произойдет исключение.

    ---
    OlegGel, огромное спасибо! Это действительно то что нужно, судя по описаниям с msdn. Буду разбираться..
    11 января 2010 г. 10:51
  • чет совсем затупил я)
    11 января 2010 г. 10:53