2013-03-21 3 views
1

Как известно, сеансы 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, если другой сеанс изменил его тем временем или должен был быть выселен/перезагружен.

Таким образом, все темы должны использовать тот же сеанс. Каков наилучший способ сделать это?

  1. Использование сохранения очереди в исходной нити (поточно реализации), который опрашивается в цикле (вместо EndInvoke()) от основного потока. Детские потоки могут вставлять объекты NHibernate для сохранения в основном потоке.

  2. Используйте некоторый механизм обратного вызова для сохранения/очистки объектов в основной теме. Есть ли что-то подобное для обратного вызова потока UI в WPF, Control.Invoke() или BackgroundWorker?

  3. Поместить Сохранить/Очистить доступ в блокировку (сеанс)? Возможно, опасно, потому что изменение объектов NHibernate может изменить сеанс, даже если не выполняется Save()/Flush().

  4. Или я должен жить с накладными данными базы данных, чтобы загружать одни и те же объекты для отдельных сеансов в каждом потоке, высекать и перезагружать их в основном потоке, а затем делать изменения снова? [Править: плохое «решение» из-за объекта параллелизм/риск несвежих объектов]

Рассмотрим также, что приложение имеет слой бизнес-логики над NHibernate, который имеет аналогичные объекты, но отправляет его значения свойства в NHibernate объекты в собственной команде Save(), а затем их модификация и немедленное выполнение NHibernate Save()/Flush().

Редактировать: Важно, чтобы любая операция чтения на объектах NHibernate могла изменять сеанс - ленивая загрузка, изменение коллекции chilren при определенных условиях. Таким образом, действительно лучше иметь слой бизнес-объекта сверху, который синхронизирует весь доступ к объектам NHibernate. Учитывая, что операции с базой данных занимают только минимальное время потоков (в основном, случайные настройки состояния), и большинство из них относится к вычислениям, просмотру, доступу к веб-сервисам и тому подобному, потеря производительности при синхронизации уровня данных незначительна.

ответ

0

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

Вы должны соблюдать следующие рекомендации при создании NHibernate Sessions: • Никогда не создавать более одного одновременно ISession или ITransaction экземпляра на подключение к базе данных.

• Будьте предельно осторожны при создании более одного ISession за базы данных за транзакцию. Сама ISession отслеживает обновления , сделанные для загруженных объектов, поэтому другой ISession может видеть устаревшие данные.

• ISession не является потокобезопасным! Никогда не обращайтесь к одному и тому же ISession в двух одновременных потоках. ISession, как правило, только один единиц работы!

+0

ОК, это то, что я знал и что у меня есть. Мой вопрос заключается в том, как наилучшим образом сохранить объекты безопасным способом. Я видел несколько сообщений здесь по этой теме, рекомендуя многосеансовый подход (по одному на поток), но это не выглядит хорошо для меня из-за проблем с избыточными БД и StaleObject при одновременном доступе. Поэтому мне, вероятно, понадобится функция Save в основном потоке, который можно вызывать из дочерних потоков. См. Вариант 2. –

0

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

Теперь, предполагая, что ваш поток может включать одни и те же потоки, считывая одни и те же данные (но записывая разные данные), я бы предложил использовать разные сеансы - по одному на поток и используя 2nd level cache;
Кэш второго уровня хранится на уровне SessionFactory (а не в Session) и поэтому делится всеми экземплярами сеанса.

Смежные вопросы