【问题标题】:net c# lock statement in data access layernet c# lock 数据访问层中的语句
【发布时间】:2011-01-01 04:49:15
【问题描述】:

我看到一个代码,他们有这样的数据访问层:

public class CustomerDA{  

    private static readonly object _sync = new object();  
    private static readonly CustomerDA _mutex = new CustomerDA();  

    private CustomerDA(){  
    }

    public CustomerDA GetInstance(){    

        lock(_sync){      
            return _mutex;        
        }    
    }  

    public DataSet GetCustomers(){  
        //database SELECT
        //return a DataSet
    }  

    public int UpdateCustomer(some parameters){  

        //update some user
    }

}  


public class CustomerBO{  

    public DataSet GetCustomers(){  

        //some bussiness logic  
        return CustomerDA.GetInstance().GetCustomers();
    }
}

我正在使用它,但开始思考......“如果必须构建一个类似 facebook 的应用程序,其中有数十万并发用户?我会阻止每个用户做他的事情,直到前一个用户结束他的数据库东西?对于更新方法,当数据库引擎已经在数据库服务器级别管理并发时,在应用程序中锁定线程是否有用?

然后我开始考虑将锁移至 GetCustomers 和 UpdateCustomer 方法,但再想一想:“它到底有用吗?”

1 月 3 日编辑:

没关系,我错过了“GetInstance”方法中的“static”关键字。

另一件事:我的想法是,如果有另一个线程在同一个数据访问类中工作,则没有线程可以访问 _mutex 变量。我的意思是,我认为由于 _mutex 变量是从 lock 语句内部返回的,所以在“;”之前没有线程可以访问 _mutex在以下句子中达到:

return CustomerDA.GetInstance().GetCustomer();

在进行了一些跟踪之后,我意识到我做出了错误的假设。您能否确认我做出了错误的假设?

所以...我可以肯定地说我的数据访问层不需要任何锁定语句(即使在 INSERT、UPDATE、DELETE 上)并且我的 DataAccess 中的方法是静态方法还是实例方法都没有关系?

再次感谢...您的 cmets 对我非常有用

【问题讨论】:

  • 无论您在哪里看到该代码,都不要再看那里。这简直是​​糟糕的代码。忽略它。
  • 我认为编写代码的人意味着拥有一个单例 CustomerDA。 GetInstance() 处的锁并不是真正需要的,因为 _mutex 是只读的。运行 SQL 语句时不会被阻塞,因为 GetCustomer() 和 UpdateCustomer() 处没有锁定。

标签: c# locking data-access-layer


【解决方案1】:

该代码中的锁定完全没有意义。它锁定了返回一个永远不会改变的值的代码,因此没有理由在那里锁定。代码中加锁的目的是使对象成为单例,但由于它没有使用延迟初始化,所以根本不需要加锁。

将数据访问层设为单例是一个非常糟糕的主意,这意味着一次只有一个线程可以访问数据库。这也意味着类中的方法必须使用锁来确保一次只有一个线程访问数据库,否则代码将无法正常工作。

相反,每个线程都应该获得自己的数据访问层实例,并拥有自己的数据库连接。这样,数据库就可以处理并发问题,而 theads 根本不需要做任何锁定。

【讨论】:

  • 我不确定我是否同意这一点。有很多假设。 DAL 的单例并不意味着它不能使用连接池来允许多个线程并发连接。更不用说代码看起来没有正确转录。 GetInstance() 应该是静态的,但不是。他们要么认为锁只允许一个用户访问数据库,要么它曾经是双重检查锁定的错误实现,以确保只创建一个实例。
  • @Andrew Finnell:是的,带有连接池的单例类也可以,但从问题中的描述来看,目前似乎没有类似的东西。
【解决方案2】:

在需要的地方设置您的锁,以便发生并发访问。在锁/关键部分中只放入真正需要的代码。

GetInstance 不应该是静态的?

下面的伪代码解释了 GetInstance 是如何运作的:

  1. 锁定
  2. rval = _mutex
  3. 解锁
  4. 返回 rval

_mutex 是只读的,指的是非空对象,所以不能更改,为什么要加锁?

如果您的数据库提供并发管理,但在您的程序中,您创建了两个线程在您自己的域中同时写入相同的数据,同时等待数据, 您的数据库如何提供帮助?

【讨论】:

  • 你回答的最后一部分是什么意思(如果有 2 个线程同时写入相同的数据,数据库如何提供帮助)?那么,如果我将锁带到方法级别是个好主意吗?类似:“public int UpdateCustomer(){lock{ //这里有一些代码}}”
  • 不要这样想:“我总是在这里锁东西”。例如,如果出于内存备用目的而只想使用一个到数据库的连接,则必须只锁定正在使用该连接的语句,而不是其他语句。始终将锁下沉到尽可能深的函数级别,并且不要锁定可以同时运行的语句。 "public int UpdateCustomer(){ /*局部计算*/ .. lock{ /*使用全局变量,调用全局对象的非线程安全函数等 */ } /*更多的局部计算*// } }"
  • 所以,总的来说:尽可能窄的锁!如果你认为你不能更深入,问问自己哪些语句会干扰该函数调用。然后进入该函数,然后重复。当您无法缩小范围时,您将达到一个点(例如 file.write(a) 并且 write 在库中)。你明白了吗?这是避免一些死锁的好策略,因为您立即释放锁定的对象,因此无需等待不必要的锁定)。这 3 件事必须严格遵守:1)LOCK 2)GET_DATA_FROM_LOCKED 3)UNLOCK_IMMEDIATELY
  • 好吧,我把GetInstance方法的锁去掉了,我明白了我可以在需要的时候把锁拿到UpdateCustomer级别……你以为“让数据访问层成为单例”一个非常糟糕的主意”?我应该每次删除单例并返回一个新实例吗?或者我应该完全删除实例并调用像“CustomerDA.GetCustomers()”这样的静态方法?
  • 在您的情况下,静态和单例是相等的。我们更喜欢单例的原因是它的生命周期更易于处理,并且尽可能避免静态/全局数据使您的代码更易于维护。如果要存储有关每个连接的信息,例如用户名、密码、连接时间、权限等,请使用新实例。我不会说使用单例作为数据访问层总是一个坏主意,但是如果您想考虑未来并在以后对代码进行修改,那么请将数据库访问器和客户端数据分开。
猜你喜欢
  • 1970-01-01
  • 2013-02-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-31
  • 1970-01-01
相关资源
最近更新 更多