【问题标题】:Do I have to lock the database connections when multithreading?多线程时是否必须锁定数据库连接?
【发布时间】:2011-09-27 05:11:44
【问题描述】:

这是我目前用于数据库交互的类的示例:

using System;
using System.Data;
using System.Collections.Generic;

// Libraries
using log4net;
using log4net.Config;
using MySql.Data.MySqlClient;

namespace AIC
{
    class DB
    {
        private static readonly ILog _logger = LogManager.GetLogger(typeof(DB));
        private MySqlConnection _connection;
        private MySqlCommand _cmd;
        private string _server;
        private string _database;
        private string _username;
        private string _password;

        //Constructor
        public DB(string server, string database, string username, string password)
        {
            log4net.Config.XmlConfigurator.Configure();

            _server = server;
            _database = database;
            _username = username;
            _password = password;

            _connection = new MySqlConnection(string.Format("SERVER={0};DATABASE={1};UID={2};PASSWORD={3};charset=utf8;", _server, _database, _username, _password));
        }

        public bool TestConnection()
        {
            try
            {
                _connection.Open();
                _connection.Close();
                _logger.Info("Connection test, passed...");
                return true;
            }
            catch (MySqlException ex)
            {
                _logger.Error(ex.ToString());
                return false;
            }
        }

        //open connection to database
        private bool Open()
        {
            try
            {
                if (_connection.State != ConnectionState.Open)
                    _connection.Open();
                _logger.Info("Starting connection to database...");
                return true;
            }
            catch (MySqlException ex)
            {
                _logger.Error(ex.ToString());
                return false;
            }
        }

        //Close connection
        private bool Close()
        {
            try
            {
                if (_connection.State != ConnectionState.Closed)
                    _connection.Close();
                _logger.Info("Closing connection to database...");
                return true;
            }
            catch (MySqlException ex)
            {
                _logger.Error(ex.ToString());
                return false;
            }
        }

        // Some basic functions
        public bool UserExist(string user)
        {
            string query = "SELECT user_id FROM users WHERE username=@name LIMIT 1";
            if (this.Open())
            {
                try
                {
                    // Assign the connection
                    _cmd = new MySqlCommand(query, _connection);

                    // Prepare to receive params
                    _cmd.Prepare();

                    // Fill up the params
                    _cmd.Parameters.AddWithValue("@name", user);

                    // returned count bool
                    bool result = Convert.ToInt32(_cmd.ExecuteScalar()) > 0;

                    // Close connection
                    this.Close();
                    return result;
                }
                catch (MySqlException ex)
                {
                    _logger.Error(ex.ToString());
                    this.Close();
                    return false;
                }
            }
            else
            {
                _logger.Error("You must be connected to the database before performing this action");
                return false;
            }
        }

        public bool AddUser(string user)
        {
            // .... add user to database
        }

        public bool DelUser(string user)
        {
            // .... del user from database
        }

        public int CountUsers()
        {
            // .... count total users from database
        }
    }
}

目前,我没有任何打开和关闭连接的管理,因此它会始终检查数据库是否连接,执行操作并关闭它如UserExist函数所示 .

考虑到这一点,我注意到我可能会在中间关闭自己的连接或他们的事务,因为我在 2 个不同的线程中使用它。

我怀疑这个简单的类是否会以任何原因锁定我的应用程序,使其无响应或从长远来看给我带来任何麻烦?

我应该考虑、改进什么等?

希望有代码示例。

【问题讨论】:

    标签: c# mysql winforms .net-4.0 database-connection


    【解决方案1】:

    您不必锁定它们,但是可以:您必须确保 2 个线程没有同时使用同一个连接。

    同步(锁等)是一种方法;隔离是另一种(更好的,IMO)方式。如果两个线程从来没有拥有相同的连接,那么一切都很好。因此,static 连接绝不是一个好主意。

    【讨论】:

    • connection 在示例代码中不是静态的。只是记录器。
    • @Henk 我没说是;这是我提出的观点的必然结果,而不是对示例的讨论。
    • @MarcGravell 你能解释/举例说明你所说的隔离是什么意思吗?
    【解决方案2】:

    等待异常被抛出然后处理它们并不是设计多线程类的好方法。使用lock 声明将是一个好的设计。使用lock 时,您提供了一个关键区域,因此一次只允许一个线程访问资源。一旦一个线程完成使用,另一个线程就可以继续,依此类推。

    例如:

    所以它会一直检查数据库是否连接,执行操作并关闭

    如果两个线程同时尝试进入同一个方法会发生什么?一个线程检查连接是否未设置为继续,并发现连接未设置,因此继续。但是在它的进程中间和连接之前,线程上下文切换切换到另一个线程并暂停第一个线程,第二个线程依次询问是否设置了连接,它会发现它不是,所以它连接并继续。现在线程上下文切换切换到第一个线程继续执行。问题就开始了……

    但使用'lock'时的情况有所不同;一个且只有一个线程将允许访问标有lock 的方法区域。于是一个线程进入锁区并建立连接。此时另一个线程尝试访问该方法,但第一个线程仍然存在,因此第二个线程将等待第一个线程完成其工作,然后它会继续。

    【讨论】:

      【解决方案3】:

      每个线程都应该有自己的连接实例,在您的情况下可能是Db 的实例。

      但是通过根本不在您的 Db 对象中存储连接可以更好地解决问题(很多)。最好的模式是在using() {} 语句中只使用连接作为局部变量。

      目前,您的类应该实现 IDisposable(仅适用于您的 try/catch 逻辑失败的情况)。

      【讨论】:

      • Henk Holterman 所以基本上,如果我将这些函数放在同一个名称空间中调用数据库并使用它,而不是在我的情况下使用 db 对象,那么基本上就可以了吗?
      • 命名空间不起作用。尽可能保持本地连接。删除 _connection 字段。
      猜你喜欢
      • 1970-01-01
      • 2022-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-03-16
      • 1970-01-01
      相关资源
      最近更新 更多