Как создать потокобезопасный единственный экземпляр IDisposable?

StackOverflow https://stackoverflow.com/questions/1624226

Вопрос

Этот объект, похожий на соединение с БД, понадобится моему веб-приложению. Он довольно медленный для создания и будет использоваться редко, поэтому я хотел бы сохранить только один его экземпляр. Если это требуется нескольким запросам одновременно, они будут lock() объект и сериализовать свой доступ.

Чтобы сделать вещи более увлекательными, объект является IDisposable и может быть закрыт. Естественно, я буду избегать написания кода, который закрывает его, но ... вы знаете ... лучше, чем потом сожалеть, верно?

Итак, наивный & # 239; ve подход заключается в том, чтобы реализовать его примерно так:

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() это прежде, чем другой приобретет Disposed на этом. Таким образом - позже код не работает. Проверка на <=> в любом месте, где он используется, также кажется неудобной. На самом деле, <=> это тоже кажется неуклюжим, но этот экземпляр по своей сути не ориентирован на многопоточность, поэтому я ничего не могу с этим поделать.

Как бы вы это реализовали?

Это было полезно?

Решение

Для начала я бы рекомендовал избегать двойной проверки блокировки. Очень легко ошибиться - как вы и сделали в этом случае, не делая переменную volatile.

Учитывая, что вы не хотите на самом деле утилизировать объект, есть ли причина не заключать его в нечто, реализующее тот же API, но без раскрытия удаления? Таким образом, вы также можете инкапсулировать блокировку, в зависимости от того, как вы на самом деле используете объект. Другой способ сделать это - представить API в терминах Action<DBClass> - вы передаете действие, которое хотите выполнить с объектом, а помощник позаботится о создании экземпляра и соответствующей блокировке.

И последнее замечание: все это кажется несколько сложным с точки зрения тестируемости. Я не знаю, являетесь ли вы поклонником модульного тестирования и т. Д., Но одиночные игры часто затрудняют тестирование.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top