【问题标题】:What is the best way to make a variable threadsafe?使变量线程安全的最佳方法是什么?
【发布时间】:2012-08-13 14:51:48
【问题描述】:

由于代码重构而出现问题,在这种情况下最好的解决方案是什么?

问题在于 DbConnection 已从本地方法变量重构为类变量。 该应用程序是多线程的。 似乎问题在于 DbConnection 对象在它是成员变量时被共享。 什么是最好的解决方案?将其保留为本地方法变量?

public IDataReader Execute(CommandBehavior behavior, string[] parameterNames, object[] arguments)
{

        DbConnection conn = null;
        try
        {

            conn = Connection.CreateConnection();

            DbCommand cmd = conn.CreateCommand();
            cmd.CommandText = StoredProcedureName;
            cmd.CommandType = CommandType.StoredProcedure;



           //     ..................................................

            // Perform the call.                        
            return DataCachingContext.SetCachedData(call, cmd.ExecuteReader(behavior));

        }
        catch (Exception ex)
        {
            //..........................
        }
        finally
        {
            //
        }

}

澄清一下,这里是导致问题的版本。存在运行时异常,索引到 ResultSets 的问题,这很可能是由于连接被覆盖。

DbConnection _conn = null;

public IDataReader Execute(CommandBehavior behavior, string[] parameterNames, object[] arguments)
{

        try
        {

            _conn = Connection.CreateConnection();

            DbCommand cmd = _conn.CreateCommand();
            cmd.CommandText = StoredProcedureName;
            cmd.CommandType = CommandType.StoredProcedure;



           //     ..................................................

            // Perform the call.                        
            return DataCachingContext.SetCachedData(call, cmd.ExecuteReader(behavior));

        }
        catch (Exception ex)
        {
            //..........................
        }
        finally
        {
            //
        }

}

经过更多调查,看起来 DbConnection 变量已成为类变量以启用单元测试。当它是局部变量时,无法测试它的值。正在测试 DbConnection 的状态

【问题讨论】:

  • 最好的方法是研究thread-safetymultithreading。这个问题已经在这里被问了数百次了; 今天可能已经被问过了。
  • 我在这里没有看到任何问题,只是您声明 DbConnection 是一个类变量,而这里它显然是一个局部变量,您是否声明了两次?如果这不是问题,我们需要更多细节。
  • 我在您的代码中没有看到任何线程。 DataCachingContext 是共享对象吗?是static吗?
  • 您通常不会使变量线程安全。您可以访问它线程安全在特定情况下。这已经暗示了解决方案:不要以跨线程方式访问变量,如果可以避免的话(通常是这样)。这是最安全的方法。
  • 如果您可以更具体地了解您遇到的问题,您将获得更好的答案。乍一看,您似乎不会遇到您使用发布的特定代码描述的问题,并且您没有说明您是否收到错误消息或错误消息的内容。

标签: c# .net multithreading thread-safety


【解决方案1】:

我认为您正在寻找lock statement .

lock 确保一个线程不会进入代码的临界区,而另一个线程在临界区。如果另一个线程试图输入一个锁定的代码,它会等待,阻塞,直到对象被释放。

【讨论】:

  • 一定要处理好你的异常,避免不释放对象。
  • @Andre 不必要的。使用lock 会自动解决这个问题——事实上,这就是使用lock 而不是Monitor.Enter 的全部意义所在。
  • @KonradRudolph 如果using 块内发生异常,该对象是否仍会被释放?
  • @AndreCalil 重复一遍,这就是lock 语句存在的全部原因。否则,我们不需要语言结构,我们可以使用 Monitor.Enter / Monitor.Exit
  • 我的错,我的意思是lock,而不是using 声明。不管怎样,谢谢。我以为异常会改变处理流程并让对象锁定。
【解决方案2】:

应用程序是多线程的。似乎问题在于 DbConnection 对象在它是成员变量时被共享。什么是最好的解决方案?将其保留为本地方法变量?

是的,使用DbConnection 的一个很好的模式是在方法中创建和处理连接(将其存储在局部变量中)。在幕后,连接是池化的,因此这样做没有显着的开销。您还可以避免处理共享状态和锁。

问题中提供的代码与我在这里描述的完全一样。

【讨论】:

  • 经过更多调查后,看起来 DbConnection 变量已成为类变量以启用单元测试。当它是局部变量时,无法测试它的值。正在测试 DbConnection 的状态。
  • 实际上,您可以实现并将DbProviderFactory 传递给您的类,以便为单元测试生成模拟连接:msdn.microsoft.com/en-us/library/ms971499.aspx
  • 木渡,这个我不懂。你可以解释吗?现在我正在寻找类变量的替代方法,以便我可以从单元测试中测试 DbConnection 的状态。有没有办法从一些全局存储中获取连接?然后测试它的状态。
  • @learnerplates:创建派生自DbProviderFactory 的类TestDbProviderFactory 并实现所有必要的方法。您必须提供模拟对象以允许您的测试运行(例如,派生自 DbConnectionTestConnection 等等)。在测试代​​码中使用TestDbProviderFactory,在生产代码中使用真实的提供者,例如SqlClientFactory 或任何你使用的东西。您可以使用依赖注入将所需的DbProviderFactory 注入到您的类中。
【解决方案3】:

数据库连接对象通常应该是局部变量,因为数据库连接是一种相对昂贵的资源,要保持打开任何时间长度。将数据库连接作为类变量的危险在于,在实例化对象时会打开连接,然后在释放类之前保持打开状态。这会导致锁没有被及时释放,以及长时间运行的事务,这会严重影响性能。

最好在需要之前打开连接(例如,调用存储过程、执行 SQL 语句),然后立即关闭/释放它。在幕后,数据库连接通常无论如何都会被缓存,这样可以最大限度地减少重复打开和关闭连接的开销,因为当您在代码中打开一个连接时,您很可能会从缓存中获得一个已经打开的连接。

【讨论】:

    【解决方案4】:

    MSDN 文档在DbConnection 上说:

    不保证任何实例成员都是线程安全的。

    因此,在我看来,将其保留为类变量并不是一个好主意。即使您的实现可能是线程安全的,但假设这样是有风险的。您可以使用锁来避免线程问题,但这些方法通常容易扩展。因此,最好将对象保持在本地。

    如果您担心总是打开和关闭太多连接的性能,请查看SQL Server Connection Pooling (ADO.NET),它在池化实际连接而不是DbConnection 对象方面做得很好。

    【讨论】:

      【解决方案5】:

      在这两个代码 sn-ps 中,您都创建了新的连接。那么将连接作为类变量的意图是什么?从您发布的代码看来,连接应该是局部变量。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-04-17
        • 2022-01-01
        • 1970-01-01
        • 2016-12-21
        • 2012-06-17
        • 1970-01-01
        相关资源
        最近更新 更多