この解決策は、複数の発信者が「準備コード」を実行する可能性を受け入れることができない人向けです。
この手法は、データが準備されたときの「通常の」ユース ケース シナリオでロックを使用することを回避します。ロックには多少のオーバーヘッドがあります。ユースケースに当てはまる場合と当てはまらない場合があります。
パターンはif-lock-if
パターン、IIRCと呼ばれます。私はできる限りインラインに注釈を付けようとしました:
bool dataReady;
string data;
object lock = new object();
void GetData()
{
// The first if-check will only allow a few through.
// Normally maybe only one, but when there's a race condition
// there might be more of them that enters the if-block.
// After the data is ready though the callers will never go into the block,
// thus avoids the 'expensive' lock.
if (!dataReady)
{
// The first callers that all detected that there where no data now
// competes for the lock. But, only one can take it. The other ones
// will have to wait before they can enter.
Monitor.Enter(lock);
try
{
// We know that only one caller at the time is running this code
// but, since all of the callers waiting for the lock eventually
// will get here, we have to check if the data is still not ready.
// The data could have been prepared by the previous caller,
// making it unnecessary for the next callers to enter.
if (!dataReady)
{
// The first caller that gets through can now prepare and
// get the data, so that it is available for all callers.
// Only the first caller that gets the lock will execute this code.
data = "Example data";
// Since the data has now been prepared we must tell any other callers that
// the data is ready. We do this by setting the
// dataReady flag to true.
Console.WriteLine("Data prepared!");
dataReady = true;
}
}
finally
{
// This is done in try/finally to ensure that an equal amount of
// Monitor.Exit() and Monitor.Enter() calls are executed.
// Which is important - to avoid any callers being left outside the lock.
Monitor.Exit(lock);
}
}
// This is the part of the code that eventually all callers will execute,
// as soon as the first caller into the lock has prepared the data for the others.
Console.WriteLine("Data is: '{0}'", data);
}
MSDN リファレンス: