这是一个很有趣的问题。我只是在 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 团队。您可以参考这篇文章作为问题的描述。