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

C# 在存储库中锁定单个会话是否线程安全?(NHibernate),c#,.net,multithreading,nhibernate,session,C#,.net,Multithreading,Nhibernate,Session,我读过很多帖子,说多线程应用程序必须为每个线程使用单独的会话。也许我不明白锁定是如何工作的,但是如果我在所有存储库方法中对会话设置了锁定,这难道不会使单个静态会话线程安全吗 比如: 编辑: 请考虑我所写的应用程序的上下文/类型: 不是多用户的,也不是典型的用户交互,而是一个自动运行的机器人,对远程事件(如财务数据和订单更新)做出反应,并在此基础上执行任务和保存。这会间歇性地创建每秒最多10次存储的群集。通常,每次都需要保存相同的对象图。此外,在启动时,程序会将完整的数据库加载到实体对象图中。所以

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

比如:

编辑:

请考虑我所写的应用程序的上下文/类型:


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

至少有两个缺点:

  • 您正在显著降低性能。在繁忙的网络服务器上安装这一功能就像在电影院外聚集一大群人,但却让人们通过一个人范围的入口进入

  • 会话有其内部标识映射(缓存)。每个应用程序的单个会话意味着随着用户从数据库访问不同的数据,内存消耗会增加。最终,您甚至可以将整个数据库保存在内存中,这当然是行不通的。这需要调用一个方法不时地删除一级缓存。但是,现在还没有放弃缓存的好时机。您不能在请求的开始就访问,因为其他并发会话可能会受到影响


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

    至少有两个缺点:

  • 您正在显著降低性能。在繁忙的网络服务器上安装这一功能就像在电影院外聚集一大群人,但却让人们通过一个人范围的入口进入

  • 会话有其内部标识映射(缓存)。每个应用程序的单个会话意味着随着用户从数据库访问不同的数据,内存消耗会增加。最终,您甚至可以将整个数据库保存在内存中,这当然是行不通的。这需要调用一个方法不时地删除一级缓存。但是,现在还没有放弃缓存的好时机。您不能在请求的开始就访问,因为其他并发会话可能会受到影响


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

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

    大概是这样的:

    [Producer Threads]
    Edit Event -\                [Database Servicer Thread]
    Edit Event ------> Queue -> Dequeue and Apply to Session -> Database
    Edit Event -/ 
    
    我认为
    BlockingCollection
    将是这样一个实现的良好起点

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

    //假设您的工作队列定义为
    public static BlockingCollection myWorkQueue=new BlockingCollection();
    //您的eventargs看起来像这样
    公共类MyObjectUpdatedEventArgs:EventArgs{
    公共MyObject MyObject{get;set;}
    }
    //和一个事件处理程序
    公共MyObjectWasChangedEventHandler(对象发送器,MyObjectUpdatedEventArgs e){
    添加(s=>SaveOrUpdate(e.MyObject));
    }
    //然后,处理这些项的常量循环中的线程可以工作:
    public void ProcessWorkQueue(){
    var mySession=mySessionFactory.CreateSession();
    while(true){
    var nextWork=myWorkQueue.Take();
    下一网络(我的会话);
    }
    }
    //并运行上述程序:
    var dbUpdateThread=新线程(ProcessWorkQueue);
    dbUpdateThread.IsBackground=true;
    dbUpdateThread.Start();
    
    鉴于应用程序通常编辑同一个对象图,可能更合理的做法是让一个线程专用于将这些编辑应用于对象图,然后将其保存到数据库,或者让一个线程池为公共编辑队列提供服务,其中每个线程都有自己的(专用)不需要锁定的会话。查找生产者/消费者队列(要开始,请查看)

    大概是这样的:

    [Producer Threads]
    Edit Event -\                [Database Servicer Thread]
    Edit Event ------> Queue -> Dequeue and Apply to Session -> Database
    Edit Event -/ 
    
    我认为
    BlockingCollection
    将是这样一个实现的良好起点

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

    //假设您的工作队列定义为
    public static BlockingCollection myWorkQueue=new BlockingCollection();
    //您的eventargs看起来像这样
    公共类MyObjectUpdatedEventArgs:EventArgs{
    公共MyObject MyObject{get;set;}
    }
    //和一个事件处理程序
    公共MyObjectWasChangedEventHandler(对象发送器,MyObjectUpdatedEventArgs e){
    添加(s=>SaveOrUpdate(e.MyObject));
    }
    //然后,处理这些项的常量循环中的线程可以工作:
    public void ProcessWorkQueue(){
    var mySession=mySessionFactory.CreateSession();
    while(true){
    var nextWork=myWorkQueue.Take();
    下一网络(我的会话);
    }
    }
    //并运行上述程序:
    var dbUpdateThread=新线程(ProcessWorkQueue);
    dbUpdateThread.IsBackground=true;
    dbUpdateThread.Start();
    
    谢谢!请看我的编辑。根据我的观察,创建会话需要0.5秒(使用SQLite),因此使用单个会话是我提高性能的尝试。无论如何,我都会将整个数据库加载到内存中。创建一个会话大约需要20毫秒。也许您不是在创建会话,而是在重新创建ISessionFactory(当然这不是必需的)?我必须检查一下,它是否应该那么快。。但我也相信多个线程会话
    // 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();