【问题标题】:mysql connector in a multi-threaded environment多线程环境中的mysql连接器
【发布时间】:2011-12-10 16:33:40
【问题描述】:

我有一个 c# 服务器,它连接到 mysql 服务器以获取数据。这个c#服务器是一个游戏的后端服务器,它为当前登录的每个玩家都有一个开放的线程。我该如何进行mysql连接,每个线程的开放连接?对所有线程使用锁定的单个连接?

我在某处读到有一个“线程池”。真的吗?如果是这样,这是使用它的正确方法吗:

using(var conn = new MySqlConnection(DatabaseHelper.getConnectionString()))
using (var cmd = conn.CreateCommand())
{
    conn.Open();
    cmd.CommandText = "SELECT username FROM characters WHERE id=1";
    using (var reader = cmd.ExecuteReader())
    {
        while (reader.Read())
        {
            user = reader.GetString("username");
        }
    }
}

【问题讨论】:

    标签: c# mysql


    【解决方案1】:

    我认为您在这里混淆了两种不同的资源;您可能确实需要为每个登录的玩家打开一个线程(实际上是一个进程),但这并不意味着您应该为每个玩家不断打开与数据库的连接,因为数据库连接、文件句柄等内容不受管理资源,应该在你用完它们后立即释放。

    C#线程here有很好的介绍和解释。

    您应该只在需要时打开与数据库的连接。这意味着您的数据访问类可以多次实例化(这是最简单的设计),每个类都有自己的连接。如果您使用connection pooling,我觉得您实际上可能一直在询问,那么您将受益更多。转向静态数据库连接设计(其中许多人共享相同的 dao 类)可能会带来更多问题,因为您可能必须同步某些资源,确保某些变量只能通过锁定或类似的方式按顺序访问,等等。

    例如,您可以阅读有关此here 的更多信息。微软还谈到了连接池@​​987654324@的好处。

    上面的代码是连接数据库的一个很好的起点,通过包含你正在关闭的 using 语句,并在你完成连接后立即处理它;您还可以考虑以下改进:

    using(var conn = new MySqlConnection(DatabaseHelper.getConnectionString())) 
    {
      using (var cmd = conn.CreateCommand()) 
      { 
          conn.Open(); 
          cmd.CommandType = CommandType.Text;
          //Parameterize your queries!
          cmd.CommandText = "SELECT username FROM characters WHERE id=?"; //Use ? as placeholder for parameters, and add parameters in the order they appear in your query.
          cmd.Parameters.Add(new MySqlParameter("@userid", MySqlDbType.Int, userid));
    
          using(IDataReader reader = cmd.ExecuteReader())
          { 
              //You should only expect one record. You might want to test for more than 1 record.
              if (reader.Read()) 
              { 
                  user = reader.GetString(reader.GetOrdinal("username")); //Think also about null value checking.
              } 
          } 
      }
    }
    

    您可能有一个 DAO 类或用户类上的一个方法来执行此操作。例如,如果它是用户上的方法,您可能会执行以下操作:

    User myUser = new User(7);
    myUser.Load(); 
    

    在 Load 内部,您可能调用的方法之一是 PopulateFromDB(),它将包含上面的代码,并会加载该用户的所有属性。你可能还有一个做同样事情的 DAO 类:

    UserLoader loader = new UserLoader();
    string userName = loader.GetUserName(int userId);
    

    这将使用上面示例中的代码返回用户名。我希望这种方法在像 User 这样的类上,因为它在逻辑上与之相连。但是,您会冒着将 DAO 逻辑与用户业务逻辑混合在一起的风险,这完全是一个独立的话题。

    与其编写大量此类数据访问逻辑,不如考虑查看某种形式的框架,例如 ORM 或类似的框架 - SO 上的 question has already been answered for MySql。这也可以为您节省大量时间和精力,并让您专注于设计。

    【讨论】:

    • 所以,至少在短期内,你上面的例子应该是我正在使用的?
    • @will 是的,两个最重要的方面是 USING 语句,这意味着一旦你完成它就会释放未管理的资源,以及 sql 查询的 PARAMETERIZATION,这将添加一层保护您的应用免受 sql 注入。我将在答案的底部添加一些示例,说明您在实践中如何使用它。
    • 查询的参数化对许多数据库系统也有好处,因为它们也可以为参数化查询重用查询执行计划。
    • 哦,在您的示例中添加参数之后,参数添加和'{'之间是否仍然存在“使用(var reader = cmd.ExecuteReader())”?
    • 当我进行参数化更改时,我得到“where 子句中的未知列_userid”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-20
    • 2018-05-10
    • 2012-04-07
    • 1970-01-01
    相关资源
    最近更新 更多