简体   繁体   English

线程安全数据访问对象C#

[英]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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM