1

我对 EF 4.1 有疑问。我们以一种方法在项目中发现了性能问题,我开始使用 dotTrace 来解决该问题,并且发现了这种奇怪的行为: 在此处输入图像描述

当应用程序启动时 - 一切正常,首先调用该方法工作正常,但后来每次我在现场执行一些操作时,花费在 Monitor.Enter 上的时间都会增加。

我知道问题在于锁定,这意味着某些东西阻止了调用,但我不知道那里发生了什么。如果我有 EF 4.1 的源代码,我可以尝试调试整个事情,但这是我要做的最后一件事。您能否给我一些提示在哪里看,什么可能会影响这种行为?我首先想到的当然是交易,但我找不到这个项目中使用的任何交易,至少是明确的!欢迎您写下您的任何想法,因为我在互联网上找不到任何类似的问题。

谢谢你。

4

2 回答 2

2

这是一个很有趣的问题。我只是在 Reflector .NET 的帮助下进行了小型快速分析(您也可以使用 ILSpy、JustDecompile 或 DotPeek 来查看任何未混淆的 .NET 源代码)。分析可能不正确或基于不正确的假设,因此请将其考虑在内。

让我们从InitializeDatabaseAction

private void InitializeDatabaseAction(Action<InternalContext> action)
{
    Func<Tuple<DbCompiledModel, string>, RetryAction<InternalContext>> valueFactory = null;
    if (!this._inDatabaseInitialization)
    {
        try
        {
            this._inDatabaseInitialization = true;
            if (valueFactory == null)
            {
                // Delegate to create a new instance of RetryAction
                valueFactory = t => new RetryAction<InternalContext>(action);
            }
            // InitializeDatabases is ConcurrentDictionary - it stores information
            // about all compiled models and they related database connection strings
            // This call will try to get existing RetryAction for the model and if
            // it doesn't exists it will use current valueFactory to create
            // a new instance and add it to dictionary. It will also return
            // that value and execute its PerformAction operation.
            // If you have just one context and one database you will have only
            // single record in the concurrent dictionary but every creation
            // of your DbContext will go through this call to ensure that database
            // is initialized. This code is executed when your context is used
            // for data retrieval or persistence for the first time.
            InitializedDatabases.GetOrAdd(Tuple.Create<DbCompiledModel, string>(this._model, this._internalConnection.ConnectionKey), valueFactory).PerformAction(this);
        }
        finally
        {
            this._inDatabaseInitialization = false;
        }
    }
}

现在让我们检查RetryAction类:

/// <summary>
/// Adapted from Lazy<> to allow the initializer to take an input object and 
/// to do one-time initialization that only has side-effects and doesn't 
/// return a value. 
/// </summary>
internal class RetryAction<TInput>
{
    // Fields
    private Action<TInput> _action;
    private readonly object _lock;

    // Methods
    public RetryAction(Action<TInput> action)
    {
        this._lock = new object();
        this._action = action;
    }

    /// <summary>
    /// Performs the action unless it has already been successfully 
    /// performed before. 
    /// </summary> 
    public void PerformAction(TInput input)
    {
        // Here we have Monitor.Enter
        lock (this._lock)
        {
            if (this._action != null)
            {
                Action<TInput> action = this._action;
                this._action = null;
                try
                {
                    action(input);
                }
                catch (Exception)
                {
                    this._action = action;
                    throw;
                }
            }
        }
    }
}

如果您有许多并发线程(您的 ASP.NET MVC 应用程序负载很重)并且您正在创建大量 DbContext 实例,那么lock执行中的操作对于您的吞吐量来说确实是一个问题。我相信这可以被视为一个错误,并且它具有非常简单的修复,可以在大多数情况下提高吞吐量:

public void PerformAction(TInput input)
{     
    // This is known as Double-Checked Locking  
    if (this._action != null)
    {
        lock (this._lock)
        {
            if (this._action != null)
            { 
                ...
            }
        }
    }
}

跟踪中的第二个问题遵循相同的问题。

我建议您再次验证这确实是您的问题的根源(对于一些调用来说应该不是问题,因为锁争用是重负载的问题)并在MS Connect上打开错误或将其发布到ADO.NET 团队。您可以参考这篇文章作为问题的描述。

于 2012-04-19T09:12:03.980 回答
0

我在我们的代码中发现了一个问题。一如既往,对一切产生巨大影响的小事业。

正如我已经说过的,在应用程序中存在一个奇怪的问题——对 MVC 应用程序的每次调用,因此对 EntityFramework 的调用都在单独的 AppDomain 中。EF CodeFirst 为每个 AppDomain 构建和编译模型一次,在我的情况下,它每次都在构建和编译模型,因为它是一个单独的 AppDomain。原因很简单,其他团队中的某个人已将文件系统日志记录添加到被调用的程序集,并且对该程序集的类的每次调用都会更改文件并且 WebServer 正在重新启动应用程序。我刚刚关闭了日志记录,它解决了问题。现在我没有这个性能问题。

于 2012-04-21T19:46:58.793 回答