У меня есть пара улучшений для вашего подхода.
1) Вам нужно добавить try-finally
блок, чтобы установить _beingUsed = false
гарантированно. В противном случае, если трудоемкий код выдаст исключение, ни один вызывающий не сможет запустить этот код.
2) Поскольку код ниже первой блокировки доступен только для одного абонента, я не вижу необходимости во второй блокировке.
Вот улучшенный код:
public ErrorCodes SomeFunction()
{
lock(_lock)
{
if(_beingUsed)
return ErrorCodes.BeingUsed;
_beingUsed = true;
}
// here can enter only one caller
try
{
// Time consuming code that is not thread safe.
return ErrorCodes.OK;
}
finally
{
_beingUsed = false;
}
}
Если производительность очень критична, вы можете рассмотреть подход без блокировки:
int _flag = 0;
public ErrorCodes SomeFunction()
{
if (Interlocked.Exchange(ref _flag, 1) == 0)
{
// here can enter only one caller
try
{
//process long operation
return ErrorCodes.OK;
}
finally
{
Interlocked.Exchange(ref _flag, 0);
}
}
else
{
//immediately return
return ErrorCodes.BeingUsed;
}
}
Но этот код выглядит более сложным и требует понимания программирования без блокировки.