在存储库线程中锁定单个会话是否安全? (NHibernate的)

时间:2012-07-25 15:29:05

标签: c# .net multithreading nhibernate session

我读了很多帖子说多线程应用程序必须为每个线程使用一个单独的会话。也许我不明白锁定是如何工作的,但是如果我在所有存储库方法中对会话进行锁定,是否会使单个静态会话线程安全?

像:

public void SaveOrUpdate(T instance)
{
    if (instance == null) return;
    lock (_session)
        using (ITransaction transaction = _session.BeginTransaction())
        {
            lock (instance)
            {
                _session.SaveOrUpdate(instance);
                transaction.Commit();
            }
        }
}

编辑:

请考虑我正在撰写的申请的背景/类型:

不是多用户,不是典型的用户交互,而是自运行机器人对财务数据和订单更新等远程事件作出反应,基于此执行任务和保存。间歇性地,这可以创建每秒最多10次保存的集群。通常,每次都需要保存相同的对象图。此外,在启动时,程序会将完整数据库加载到实体 - 对象 - 图形中。所以它基本上只读取一次,然后在运行时执行SaveOrUpdates。

2 个答案:

答案 0 :(得分:4)

鉴于应用程序通常编辑相同的对象图,或许将单个线程专用于将这些编辑应用于对象图,然后将它们保存到数据库,或者可能是一组线程服务更有意义一个公共的编辑队列,每个线程都有自己的(专用)会话,它不需要锁定。查找生产者/消费者队列(开始,查看here)。

这样的事情:

[Producer Threads]
Edit Event -\                [Database Servicer Thread]
Edit Event ------> Queue -> Dequeue and Apply to Session -> Database
Edit Event -/ 

我认为BlockingCollection<Action<Session>>对于这样的实现来说是一个很好的起点。

这是一个粗略的例子(注意这显然是未经测试的):

// Assuming you have a work queue defined as 
public static BlockingCollection<Action<Session>> myWorkQueue = new BlockingCollection<Action<Session>>();

// and your eventargs looks something like this
public class MyObjectUpdatedEventArgs : EventArgs {
    public MyObject MyObject { get; set; }
}

// And one of your event handlers
public MyObjectWasChangedEventHandler(object sender, MyObjectUpdatedEventArgs e) {
    myWorkQueue.Add(s=>SaveOrUpdate(e.MyObject));
}

// Then a thread in a constant loop processing these items could work:
public void ProcessWorkQueue() {
    var mySession = mySessionFactory.CreateSession();
    while (true) {
        var nextWork = myWorkQueue.Take();
        nextWork(mySession);
    }
}

// And to run the above:
var dbUpdateThread = new Thread(ProcessWorkQueue);
dbUpdateThread.IsBackground = true;
dbUpdateThread.Start();

答案 1 :(得分:2)

至少有两个缺点:

  1. 您正在显着降低性能。在忙碌的网络服务器上拥有这个就像在电影院外面挤出一群人,但让人们通过一个人的入口进入。

  2. 会话具有内部标识映射(缓存)。每个应用程序的单个会话意味着当用户从数据库访问不同的数据时,内存消耗会增加。最终你甚至可以在内存中结束整个数据库,这当然是行不通的。这需要调用一个方法来不时删除第一级缓存。但是,删除缓存没有好时机。您只是无法在请求开始时插入,因为其他并发会话可能会受此影响。

  3. 我相信人们会增加其他缺点。