それは非常に興味深い問題です。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)
{
...
}
}
}
}
トレースの2番目の問題は、同じ問題の後に続きます。
これが本当に問題の原因であることをもう一度確認し(ロックの競合は高負荷の問題であるため、数回の呼び出しでは問題にならないはずです)、MS Connectでバグを開くか、 ADO.NETチームに投稿することをお勧めします。問題の説明としてこの投稿を参照できます。