Нужно ли блокировать соединения с базой данных при многопоточности? - PullRequest
2 голосов
/ 02 июля 2011

Вот пример класса, который я сейчас использую для взаимодействия с базой данных:

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 разных потоках.

Мое сомнение здесь в том, может ли этот простой класс заблокировать мое приложение по любой причине, что сделает его не отвечающим или вызовет какие-либо проблемы в долгосрочной перспективе?

Что я должен рассмотреть, улучшить и т. Д.? * 10101

Буду признателен за примеры кода.

Ответы [ 3 ]

2 голосов
/ 02 июля 2011

Каждый поток должен иметь свой собственный экземпляр соединения, в вашем случае, вероятно, экземпляр Db.

Но проблема будет решена (намного) лучше, если вообще не хранить соединение в ваших объектах Db. Лучше всего использовать соединения как локальные переменные в операторе using() {}.

В настоящее время ваш класс должен реализовывать IDisposable (только для случая, когда ваша логика try / catch не работает).

1 голос
/ 02 июля 2011

Вам не нужно блокировать их, но да: вы должны убедиться, что 2 потока не используют одно и то же соединение одновременно.

Синхронизация (блокировки и т. Д.) - это один способ сделать это; Изоляция - это еще один (лучше IMO) способ. Если у двух потоков нет одинакового соединения, тогда все хорошо. По этой причине соединение static никогда не является хорошей идеей.

1 голос
/ 02 июля 2011

Ожидание создания исключений и их последующая обработка не являются хорошим способом разработки многопоточного класса. хороший дизайн будет с использованием lock оператора. при использовании lock вы предоставляете критических областей , поэтому только один поток может получить доступ к ресурсам одновременно. как только один поток завершает свое использование, другой может продолжаться и т. д.

Например:

, поэтому он всегда будет проверять, подключена база данных или нет, выполнить действие и закрыть

что произойдет, если два потока попытаются одновременно войти в один и тот же метод? один поток проверяет, не установлено ли соединение для продолжения, и обнаруживает, что соединение не установлено, поэтому оно продолжается. но в середине его процесса и до его подключения переключение контекста потока переключается на другой поток и приостанавливает первый, второй поток в свою очередь спрашивает, было ли установлено соединение, и обнаруживает, что это не так, поэтому он подключается и продолжить. Теперь переключение контекста потока переключается на первый поток, чтобы продолжить его выполнение. и начинается проблема ...

Но сценарий отличается при использовании «блокировки»; Один и только один поток получит доступ к области метода, отмеченной lock. Таким образом, один поток входит в область блокировки и устанавливает соединение. в то время другой поток пытается получить доступ к методу, но первый все еще там, поэтому второй будет ждать, пока первый завершит свою работу, а затем продолжит работу.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...