У меня есть система, которая берет Образцы. У меня есть несколько клиентских потоков в приложении, которые интересуются этими Образцами, но фактический процесс взятия Образца может только произойти в одном контексте. Это достаточно быстро, что это хорошо для него для блокирования обработки вызовов, пока Выборка не сделана, но достаточно медленный, что я не хочу нескольких потоков, накапливающих запросы. Я придумал этот дизайн (разделенный вниз к минимальным деталям):
public class Sample
{
private static Sample _lastSample;
private static int _isSampling;
public static Sample TakeSample(AutomationManager automation)
{
//Only start sampling if not already sampling in some other context
if (Interlocked.CompareExchange(ref _isSampling, 0, 1) == 0)
{
try
{
Sample sample = new Sample();
sample.PerformSampling(automation);
_lastSample = sample;
}
finally
{
//We're done sampling
_isSampling = 0;
}
}
return _lastSample;
}
private void PerformSampling(AutomationManager automation)
{
//Lots of stuff going on that shouldn't be run in more than one context at the same time
}
}
Действительно ли это безопасно для использования в сценарии, который я описал?
Да, это выглядит безопасным, потому что int
является атомарный тип здесь. Но я бы все же посоветовал заменить
private static int _isSampling;
на
private static object _samplingLock = new object();
и использовать:
lock(_samplingLock)
{
Sample sample = new Sample();
sample.PerformSampling(automation);
_lastSample = sample;
}
Просто потому, что это рекомендуемый шаблон, а также гарантирует, что весь доступ к _lastSample обрабатывается правильно.
NB: Я ожидал сопоставимой скорости, блокировка использует управляемый класс Monitor, который внутренне использует Interlocked.
Редактировать:
Я пропустил аспект отсрочки, вот другая версия:
if (System.Threading.Monitor.TryEnter(_samplingLock))
{
try
{
.... // sample stuff
}
finally
{
System.Threading.Monitor.Exit(_samplingLock);
}
}
Обычно я объявляю volatile bool и делаю что-то вроде:
private volatile bool _isBusy;
private static Sample _lastSample;
private Sample DoSomething()
{
lock(_lastSample)
{
if(_isBusy)
return _lastSample;
_isBusy = true;
}
try
{
_lastSample = new sameple//do something
}
finally
{
lock(_lastSample)
{
_isBusy = false;
}
}
return _lastSample;
}