Существует такой объект, похожий на DB-соединение, который понадобится моему веб-приложению. Это довольно медленно создавать и будут использоваться редко, поэтому я хотел бы сохранить только один экземпляр этого объекта. Если несколько запросов нуждаются в этом одновременно, они будут lock()
объекта и сериализуют их доступ.Как создать потокобезопасный единственный экземпляр IDisposable?
Чтобы сделать вещи более увлекательными, объект является IDisposable и может быть закрыт. Естественно, я избегу писать код, который его закрывает, но ... вы знаете ... лучше безопасно, чем жаль, не так ли?
Таким образом, наивный подход был бы реализовать это что-то вроде этого:
private static DBClass _Instance;
private static object _DBLock = new object();
public DBClass GetDB()
{
if (_Instance == null)
lock (_DBLock)
if (_Instance == null)
_Instance = CreateInstance();
lock (_Instance)
if (_Instance.Disposed)
_Instance = CreateInstance();
return _Instance;
}
Он будет использоваться как:
lock (var Conn = Global.GetDB())
{
// Use Conn here.
}
Это вероятно, будет работать большую часть времени, но я см. открытие, которое может быть использовано - если два потока называют это одновременно, они получают один и тот же экземпляр одновременно, а затем все еще могут быть Dispose()
, прежде чем другой приобретет на нем lock()
. Таким образом - более поздний код выходит из строя. Проверка на Disposed
в каждом месте, где она используется, также кажется неудобной. Фактически, lock()
ing тоже кажется неудобным, но экземпляр не является по сути потокобезопасным, поэтому я ничего не могу с этим поделать.
Как вы это реализуете?
Проблема заключается в том, что эта вещь возвращает другие объекты, которые содержат ссылку на нее и выполняют операции над ней. Если я не закрою пару сотен классов, это не сработает. Подход Action <> заслуживает внимания, но мне интересно, стоит ли ему увеличивать беспорядок в коде. Мне нужен элегантный подход, но это похоже на другой путь ... –
Кстати. Я все равно использую lock(). Почему нужна летучесть? –
Возможно, из-за * вторичной блокировки, вы на самом деле все в порядке, но, учитывая, что у вас есть две разные блокировки, мне совершенно не ясно, что это действительно * хорошо. Я лично использовал бы единую блокировку для всего - поместите весь метод в оператор блокировки, используя '_DBLock'. Тонкости модели памяти довольно сложны. Аналогично, если другой поток использует объект * без * блокировки, но не располагает, нет никакой гарантии, что вы его заметите и создадите новый экземпляр. По существу, похоже, что этот дизайн открывает много возможностей для множества тонких ошибок. –