Требуется очень быстрая потокобезопасная коллекция или база данных в памяти

#.net #multithreading #collections #thread-safety #in-memory-database

#.net #многопоточность #Коллекции #потокобезопасность #база данных в памяти

Вопрос:

Я получаю данные из внешнего приложения:

класс DataItem
{
 открытый строковый ключ;
 публичный атрибут int 1;
 общедоступный строковый Атрибут2; 
}

Один поток хранит его в коллекции. Другие потоки (3-10) запрашивают сбор данных по ключу (90%) и атрибутам (10%).

Каков наилучший способ реализовать это, если у меня есть 10, 100, 1000 предметов в коллекции?

Комментарии:

1. как насчет параллельной хэш-карты? Также вы запрашиваете только по ключу или a1 / a2?

2. уникальны ли атрибуты / ключ?

Ответ №1:

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

Комментарии:

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

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

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

Ответ №2:

Если коллекция неизменяема (доступна только для чтения, никогда не меняется) после инициализации, и коллекция инициализируется до того, как к ней смогут добраться какие-либо потоки, вам не нужно делать ничего особенного. Несколько потоков могут читать из коллекции или словаря одновременно без каких-либо проблем.

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

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

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

Комментарии:

1. 1 согласовано. Вы могли бы поддерживать две коллекции: оригинал и копию. Оригинал является динамическим, а копия всегда доступна только для чтения. Когда вы хотите опубликовать новое состояние исходной коллекции, просто скопируйте ее и назначьте новую ссылку соответствующей переменной. Убедитесь, что эта переменная отмечена volatile , и вам не понадобятся дополнительные механизмы синхронизации. И это будет работать для любого типа коллекции.

2. @Brian: Да, я много раз использовал шаблон с двумя списками, когда общая коллекция время от времени обновлялась во время использования (два списка не нужны для неизменяемых коллекций). Используйте InterlockedExchange для замены указателей списка, чтобы гарантировать атомарность чтения-изменения-записи в глобальной переменной указателя.

Ответ №3:

Предназначена ли коллекция в памяти только для чтения? Это изменит то, что вы в конечном итоге используете.

Мои рекомендации —
Только для чтения: используйте ConcurrentDictionary
Чтение и запись: используйте DataSet

Лучшей параллельной или потокобезопасной моделью, на мой взгляд, был бы DataSet — см. ADO.Сеть решает проблему параллелизма данных и набора данных MSDN. Набор данных был разработан для обработки хранения данных в памяти для нескольких клиентов. ОБРАТИТЕ внимание, что говорится в MSDN:

Этот тип безопасен для многопоточных операций чтения. Необходимо синхронизировать все операции записи.

У вас действительно есть альтернатива DataSet, как предлагает Брайан Гидеон — ConcurrentDictionary.

С помощью DataReader вы можете заполнять пользовательские объекты, например DataItem , непосредственно из DataReader.

В любом случае, оба этих решения обеспечат вам быстрый и одновременный доступ к данным в памяти.

Комментарии:

1. Почему ConcurrentDictionary должен быть доступен только для чтения? ConcurrentDictionary обладает следующими преимуществами по сравнению с DataSet: более быстрый поиск и отсутствие необходимости ручной синхронизации операций чтения или записи (при условии, что вы не изменяете свойства отдельных значений, хранящихся в словаре).

2. @Joel: предполагая, что вы не изменяете свойства отдельных значений, хранящихся в словаре … точно…

3. Я думаю, мне не следует предполагать, что неизменяемые типы хранятся в этом словаре. Однако это намного упрощает задачу — и это не означает, что коллекция доступна только для чтения, поскольку вы можете легко добавлять, удалять или заменять неизменяемые значения.

4. @Джоэл: совершенно верно … Я не хотел ничего предполагать в своем ответе, поскольку OP явно не указывал какую-либо форму неизменности, только для чтения и т.д.