【问题标题】:Lock on Static List or access by Key锁定静态列表或按键访问
【发布时间】:2019-09-17 10:44:40
【问题描述】:

请给专家意见,参考下面基于键值对的静态排序列表。

Method1 用于关闭连接使用使用键访问排序列表的方法。

Method2 用于关闭连接,在 Sorted List 上使用 lock 语句并通过索引访问它。

请指导哪种方法更好,因为成千上万的用户同时在 Web 应用程序上创建了数千个连接。注意,不加锁的索引访问会引发Index out of bound exception

internal class ConnA
{
    static internal SortedList slCons = new SortedList();   

    internal static bool CreateCon(string ConnID)
    {
        string constring = "sqlconnectionstring_containing_DataSource_UserInfo_InitialCatalog";         
        SqlConnection objSqlCon = new SqlConnection(constring);
        objSqlCon.Open();
        bool connSuccess = (objSqlCon.State == ConnectionState.Open) ? true : false;

        if (connSuccess && slCons.ContainsKey(ConnID) == false)
        {
            slCons.Add(ConnID, objSqlCon);
        }
        return connSuccess;
    }

    //Method1
    internal static void CloseConnection(string ConnID)
    {
        if (slCons.ContainsKey(ConnID))
        {
            SqlConnection objSqlCon = slCons[ConnID] as SqlConnection; 
            objSqlCon.Close();
            objSqlCon.Dispose();
            objSqlCon.ResetStatistics();
            slCons.Remove(ConnID);
        }
    }

    //Method2
    internal static void CloseConnection(string ConnID)
    {
        lock (slCons)
        {
            int nIndex = slCons.IndexOfKey(ConnID);
            if (nIndex != -1)
            {
                SqlConnection objSqlCon = (SqlConnection)slCons.GetByIndex(nIndex);
                objSqlCon.Close();
                objSqlCon.Dispose();
                objSqlCon.ResetStatistics();
                slCons.RemoveAt(nIndex);
            }
        }       
    }

internal class UserA
{
    public string ConnectionID { get { return HttpContext.Current.Session.SessionID; } }

    private ConnA objConnA = new objConnA();

    public void ConnectDB()
    {
        objConnA.CreateCon(ConnectionID));            
    }

    public void DisConnectDB()
    {
        objConnA.CloseConnection(ConnectionID));            
    }
}

【问题讨论】:

  • 不要将数据库连接存储在静态列表中。您正在手动进行某种连接池,不要重新发明轮子,让 .net 框架在不再使用时释放其资源。
  • 不要锁定 slCons:更喜欢创建一个唯一的对象,例如“object locker = new object();”并锁定它,不做任何其他事情。
  • 每当您有一个由多个进程共享的列表时,您必须在添加/删除时始终锁定,以防止出现索引错误等共享问题。该过程可以在添加/删除中间切换,最终访问错误的索引。
  • @jdweng,对。即使我们通过key访问我们也需要锁定?

标签: c# static locking keyvaluepair sortedlist


【解决方案1】:

SortedList 的访问不是线程安全的。

CreateCon 中,两个线程可以同时访问它:

if (connSuccess && slCons.ContainsKey(ConnID) == false)

两个线程都可以确定密钥不存在,然后两个线程都尝试添加它,因此其中一个失败了。

在方法2中:

当它被调用时 - slCons.RemoveAt(nIndex); - lock 保证对同一方法的另一个调用不会删除另一个连接,这很好。但是没有什么能保证另一个线程不会调用CreateCon 并插入一个新的连接字符串,从而更改索引,以便nIndex 现在引用集合中的不同项目。您最终会关闭、处理和删除错误的连接字符串,可能是另一个线程仍在使用的连接字符串。

您似乎正在尝试编排,以确保在多个操作中使用单个连接字符串。但是没有必要介绍这种复杂性。无论什么类或方法需要一个连接,它都不需要与这个集合和这些方法协作。您可以让他们每个人在需要时打开一个连接,并在完成后处理该连接。

这很昂贵,但这就是框架实现connection pooling 的原因。从您的代码连接的角度来看,正在创建、打开、关闭和处置。

但在幕后,“关闭”连接并没有真正关闭,至少不是马上关闭。它实际上是保持开放的。如果在短时间内,您使用相同的连接字符串“打开”另一个连接,您实际上再次获得相同的连接,该连接仍然处于打开状态。这就是减少打开和关闭的连接数量的方式,而无需我们手动管理它。

这反过来又使我们不必做看起来像您正在做的事情。如果我们在一个连接上打开一个事务,这可能会有所不同,然后我们必须确保在同一个连接上执行多个操作。但即便如此,传递 connection 而不是 ID 可能会更清晰、更简单。

【讨论】:

  • 感谢您的详细解答。如果在列表中添加新项目之前我在 CreateCon 方法中有 lock(slCons) 怎么办。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多