[英]Thread Safe Data Access Object C#
I'm trying to make a thread safe Data Access Layer (kind of like a SQL Data Client wrapper). 我正在尝试使线程安全的数据访问层(类似于SQL Data Client包装器)。 What are some steps I should be making to make this thread safe, while maximizing performance. 在使性能最大化的同时,我应该采取哪些步骤使此线程安全。
For example, if i add a lock on the sqlConn before it closes the connection (since it implements IDisposable); 例如,如果我在关闭连接之前在sqlConn上添加了一个锁(因为它实现了IDisposable); what if the connection is in the middle of a transaction or query? 如果连接在事务或查询中间怎么办?
In summary, I'm trying to accomplish a thread-safe solution; 总而言之,我正在尝试完成一个线程安全的解决方案。 but at the same time, I do not want to risk any critical exceptions, or any delays. 但同时,我不想冒任何重大例外或任何延误的风险。 Is there any way I can prioritize the closing thread? 有什么办法可以使结束线程优先 ?
public class SQLWrapper : IDisposable
{
private SqlConnection _sqlConn;
public SQLWrapper(string serverName_, string dbName_)
{
SqlConnectionStringBuilder sqlConnSB = new SqlConnectionStringBuilder()
{
DataSource = serverName_,
InitialCatalog = dbName_,
ConnectTimeout = 30,
IntegratedSecurity = true,
};
sqlConnSB["trusted_connection"] = "yes";
this.start(sqlConnSB.ConnectionString);
}
public SQLWrapper(string connString_)
{
this.start(connString_);
}
private void start(string connString_)
{
if (string.IsNullOrEmpty(connString_) == true)
throw new ArgumentException("Invalid connection string");
**lock (this._sqlConn)**
{
this._sqlConn = new SqlConnection(connString_);
this._sqlConn.Open();
}
}
private void CloseConnection()
{
**lock (this._sqlConn)**
{
this._sqlConn.Close();
this._sqlConn.Dispose();
this._sqlConn = null;
}
}
}
The step you should do is: 您应该执行的步骤是:
NOT making it thread safe. 没有使其线程安全。
Simple. 简单。
Every thread should have it's own copy, with locking / synchronization happening on the database. 每个线程都应该有自己的副本,并且数据库上会发生锁定/同步。
THen it will also scale across computers. 然后它还将在计算机之间扩展。
This is the standard approach for the last 20 years or so. 这是最近20年左右的标准方法。
So, every thread creates a new SqlWrapper and everything is fine. 因此,每个线程都会创建一个新的SqlWrapper,一切都很好。
The database performs connection pooling for you; 数据库为您执行连接池; lean on it as much as you can. 尽可能地依靠它。 You really shouldn't require locking. 您确实不需要锁定。
Option 1. 选项1。
SqlConnection is not encapsulated by the DAO class; SqlConnection不是由DAO类封装的; appropriate using structures and storage of the connection string is required at the method level. 在方法级别需要适当的使用结构并存储连接字符串。
public class SomeDAO { private readonly string _connectionString; public SomeDAO(string dsn) { _connectionString = dsn; } public IEnumerable<AssetVO> DoWork() { const string cmdText = "SELECT [AssetId] FROM [dbo].[Asset]"; using (var conn = new SqlConnection(_connectionString)) { conn.Open(); using (var cmd = new SqlCommand(cmdText, conn)) using (var dr = cmd.ExecuteReader()) { while (dr.Read()) { yield return new AssetVO { AssetId = Guid.Parse(dr["AssetId"].ToString()), }; } } } } }
Option 2. 选项2。
The SqlConnection is a class member; SqlConnection是一个类成员。 it's state is carefully maintained by helper methods. 它的状态是通过辅助方法精心维护的。 Using syntax is used for SqlDataReader and SqlCommand. 使用语法用于SqlDataReader和SqlCommand。
public class SomeDAO : IDisposable { #region backing store private readonly SqlConnection _connection; #endregion public SomeDAO(string dsn) { _connection = new SqlConnection(dsn); } public SqlConnection OpenConnection() { if (_connection.State != ConnectionState.Closed) _connection.Open(); return _connection; } public void CloseConnection() { if (_connection.State != ConnectionState.Closed) _connection.Close(); } public IEnumerable<AssetVO> DoWork() { const string cmdText = "SELECT [AssetId] FROM [dbo].[Asset]"; try { using (var cmd = new SqlCommand(cmdText, OpenConnection())) using (var dr = cmd.ExecuteReader()) { while (dr.Read()) { yield return new AssetVO { AssetId = Guid.Parse(dr["AssetId"].ToString()), }; } } } finally { CloseConnection(); } } #region Implementation of IDisposable /// <summary> /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. /// </summary> /// <filterpriority>2</filterpriority> public void Dispose() { _connection.Dispose(); } #endregion }
Both solutions survive a threaded test without the need for explicit locking. 两种解决方案都可以通过线程测试,而无需显式锁定。
private static volatile bool _done;
private static void Main()
{
#region keyboard interrupt
ThreadPool.QueueUserWorkItem(delegate
{
while (!_done)
{
if (!Console.KeyAvailable) continue;
switch (Console.ReadKey(true).Key)
{
case ConsoleKey.Escape:
_done = true;
break;
}
}
});
#endregion
#region start 3 threads in the pool
ThreadPool.QueueUserWorkItem(DatabaseWorkerCallback);
ThreadPool.QueueUserWorkItem(DatabaseWorkerCallback);
ThreadPool.QueueUserWorkItem(DatabaseWorkerCallback);
#endregion
Thread.Sleep(Timeout.Infinite);
}
private static void DatabaseWorkerCallback(object state)
{
Console.WriteLine("[{0}] Starting", Thread.CurrentThread.ManagedThreadId);
Thread.Sleep(1000);
while (!_done)
{
using (var dao = new SomeDAO(Properties.Settings.Default.DSN))
{
foreach (var assetVo in dao.DoWork())
Console.WriteLine(assetVo);
}
}
Console.WriteLine("[{0}] Stopping", Thread.CurrentThread.ManagedThreadId);
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.