Я использую поточно-ориентированную стороннюю библиотеку для извлечения данных из истории.
Режим работы для типичного сценария следующий:
Library instance;
Result[] Process(string[] itemNames) {
var itemsIds = instance.ReserveItems(itemNames);
Result[] results = instance.ProcessItems(itemIds);
instance.ReleaseItems(itemIds);
return results;
}
Library
- это класс, создание которого требует больших затрат, поэтому он используется здесь как синглтон (instance
) и отлично работает с несколькими потоками.
Однако иногда я замечаю, что Результат помечается как неудачный («элемент не найден»), когда несколько потоков пытаются выполнить Process
с массивом itemNames, который разделяет некоторые общие элементы . Поскольку библиотека очень плохо документирована, это было неожиданно.
При интенсивном ведении журнала я пришел к выводу, что поток может освободить элемент в то же время, когда другой собирается его обработать.
После пары писем поставщику библиотеки я узнал, что instance
делит список зарезервированных элементов между потоками, и что необходимо синхронизировать вызовы ...
Компиляция некоторой части библиотеки подтвердила это: существует список m_items уровня класса, который используется и ReserveItems, и ReleaseItems.
Итак, я представляю следующие отходы:
Result[] Process(string[] itemNames) {
lock(instance) {
var itemsIds = instance.ReserveItems(itemNames);
Result[] results = instance.ProcessItems(itemIds);
instance.ReleaseItems(itemIds);
return results;
}
}
Но мне это кажется слишком жестоким.
Поскольку эта библиотека отлично работает, когда разные элементы обрабатываются несколькими потоками, как можно выполнить более детальную синхронизацию и избежать снижения производительности?
РЕДАКТИРОВАТЬ - 2018-11-09
Я заметил, что все тело метода ProcessItems
библиотеки заключено в оператор блокировки ...
Так что любая попытка точной синхронизации вокруг этого тщетна. Я закончил тем, что включил свое тело метода Process
в оператор блокировки, снижение производительности, как и ожидалось, вообще не ощущается.