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

线程安全数据访问对象C#,c#,sql,multithreading,sqlclient,C#,Sql,Multithreading,Sqlclient,我试图创建一个线程安全的数据访问层(有点像SQL数据客户端包装器)。我应该采取哪些步骤来确保线程安全,同时最大限度地提高性能 例如,如果我在sqlConn关闭连接之前添加了一个锁(因为它实现了IDisposable);如果连接处于事务或查询的中间位置,则如何?< /P> 总之,我正在尝试实现一个线程安全的解决方案;但同时,我不想冒任何重大异常或任何延迟的风险。有什么方法可以让我对结束线程进行优先级排序吗 public class SQLWrapper : IDisposable {

我试图创建一个线程安全的数据访问层(有点像SQL数据客户端包装器)。我应该采取哪些步骤来确保线程安全,同时最大限度地提高性能

例如,如果我在sqlConn关闭连接之前添加了一个锁(因为它实现了IDisposable);如果连接处于事务或查询的中间位置,则如何?< /P> 总之,我正在尝试实现一个线程安全的解决方案;但同时,我不想冒任何重大异常或任何延迟的风险。有什么方法可以让我对结束线程进行优先级排序吗

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;
            }
        }
    }

您应该执行的步骤是:

不能保证线程安全

简单

每个线程都应该有自己的副本,并在数据库上进行锁定/同步

然后,它还将在计算机上扩展

这是过去20年左右的标准方法


因此,每个线程都会创建一个新的SqlWrapper,一切正常。

数据库会为您执行连接池;尽可能地依靠它。你真的不应该要求锁定

备选案文1

  • SqlConnection没有被DAO类封装;在方法级别,需要使用连接字符串的适当结构和存储

    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()),
                        };
                    }
                }
            }
        }
    }
    

    每个线程可能只有一个连接。然后,所有的线程安全问题都消失了。
    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
    }
    
        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);
        }