Как известно, сеансы NHibernate не являются потокобезопасными. Но у нас есть путь к коду, разделенный на несколько длинных потоков, все с использованием объектов, загруженных в начальный поток.Лучший образец многопоточности NHibernate?
using (var session = factory.OpenSession())
{
var parent = session.Get<T>(parentId);
DoSthWithParent(session, parent);
foreach (var child in parent.children)
{
parallelThreadMethodLongRunning.BeginInvoke(session, child);
//[Thread #1] DoSthWithChild(child #1) -> SaveOrUpdate(child #1) + Flush()
//[Thread #2] DoSthWithChild(child #2) -> SaveOrUpdate(child #2) + Flush()
//[Thread #3] DoSthWithChild(child #3) -> SaveOrUpdate(child #3) + Flush()
// -> etc... changes to be persisted immediately, not all at the end.
EndInvoke();
}
DoFinalChangesOnParentAndChildren(parent);
session.Flush();
}
}
Один из способов будет сеанс для каждого потока, но для этого потребуется родительский объект, который будет перезагружен в каждом. Кроме того, последний метод также выполняет изменения в дочерних элементах и запускается в исключении StaleObjectException, если другой сеанс изменил его тем временем или должен был быть выселен/перезагружен.
Таким образом, все темы должны использовать тот же сеанс. Каков наилучший способ сделать это?
Использование сохранения очереди в исходной нити (поточно реализации), который опрашивается в цикле (вместо EndInvoke()) от основного потока. Детские потоки могут вставлять объекты NHibernate для сохранения в основном потоке.
Используйте некоторый механизм обратного вызова для сохранения/очистки объектов в основной теме. Есть ли что-то подобное для обратного вызова потока UI в WPF, Control.Invoke() или BackgroundWorker?
Поместить Сохранить/Очистить доступ в блокировку (сеанс)? Возможно, опасно, потому что изменение объектов NHibernate может изменить сеанс, даже если не выполняется Save()/Flush().
Или я должен жить с накладными данными базы данных, чтобы загружать одни и те же объекты для отдельных сеансов в каждом потоке, высекать и перезагружать их в основном потоке, а затем делать изменения снова? [Править: плохое «решение» из-за объекта параллелизм/риск несвежих объектов]
Рассмотрим также, что приложение имеет слой бизнес-логики над NHibernate, который имеет аналогичные объекты, но отправляет его значения свойства в NHibernate объекты в собственной команде Save(), а затем их модификация и немедленное выполнение NHibernate Save()/Flush().
Редактировать: Важно, чтобы любая операция чтения на объектах NHibernate могла изменять сеанс - ленивая загрузка, изменение коллекции chilren при определенных условиях. Таким образом, действительно лучше иметь слой бизнес-объекта сверху, который синхронизирует весь доступ к объектам NHibernate. Учитывая, что операции с базой данных занимают только минимальное время потоков (в основном, случайные настройки состояния), и большинство из них относится к вычислениям, просмотру, доступу к веб-сервисам и тому подобному, потеря производительности при синхронизации уровня данных незначительна.
ОК, это то, что я знал и что у меня есть. Мой вопрос заключается в том, как наилучшим образом сохранить объекты безопасным способом. Я видел несколько сообщений здесь по этой теме, рекомендуя многосеансовый подход (по одному на поток), но это не выглядит хорошо для меня из-за проблем с избыточными БД и StaleObject при одновременном доступе. Поэтому мне, вероятно, понадобится функция Save в основном потоке, который можно вызывать из дочерних потоков. См. Вариант 2. –