Как правильно добавить потокобезопасность к объекту IDisposable?

Представьте себе реализацию интерфейса IDisposable , у которого есть некоторые общедоступные методы.

Если экземпляр этого типа используется совместно несколькими потоками и один из потоков может его удалить, как лучше всего гарантировать, что другие потоки не будут пытаться работать с экземпляром после удаления? В большинстве случаев после удаления объекта его методы должны знать об этом и генерировать исключение ObjectDisposedException или, возможно, InvalidOperationException или, по крайней мере, сообщить вызывающему коду о том, что он сделал что-то неправильно. Нужна ли мне синхронизация для каждого метода - особенно вокруг проверки, если он удален? Все ли реализации IDisposable с другими общедоступными методами должны быть потокобезопасными?


Вот пример:

public class DummyDisposable : IDisposable
{
    private bool _disposed = false;

    public void Dispose()
    {
        _disposed = true;
        // actual dispose logic
    }

    public void DoSomething()
    {
        // maybe synchronize around the if block?
        if (_disposed)
        {
            throw new ObjectDisposedException("The current instance has been disposed!");
        }

        // DoSomething logic
    }

    public void DoSomethingElse()
    {
         // Same sync logic as in DoSomething() again?
    }
}
30
задан Ivaylo Slavov 19 January 2012 в 15:02
поделиться

2 ответа

Большинство реализаций BCL в Dispose не являются поточно-ориентированными. Идея состоит в том, что вызывающий Dispose должен убедиться, что никто больше не использует этот экземпляр до его удаления. Другими словами, это увеличивает ответственность за синхронизацию. Это имеет смысл, так как в противном случае теперь всем другим вашим потребителям нужно обрабатывать граничный случай, когда объект был удален, пока они его использовали.

Тем не менее, если вам нужен потокобезопасный класс Disposable, вы можете просто создать блокировку вокруг каждого открытого метода (включая Dispose) с проверкой на _disposed вверху. Это может усложниться, если у вас есть долгосрочные методы, в которых вы не хотите удерживать блокировку для всего метода.

14
ответ дан 28 November 2019 в 00:06
поделиться

Я предпочитаю использовать целые числа и Interlocked.Exchange или Interlocked.CompareExchange в объекте типа integer типа «disposed» или «state»; Я бы использовал enum, если бы Interlocked.Exchange или Interlocked.CompareExchange могли обрабатывать такие типы, но, увы, они не могут.

Одна вещь, о которой большинство дискуссий об IDisposable и финализаторах не упоминается, это то, что, хотя финализатор объекта не должен запускаться, пока IDisposable.Dispose () находится в процессе, у класса нет способа предотвратить объявление объектов его типа. умер, а затем воскрес. Конечно, если внешний код допускает это, очевидно, не может быть никаких требований, чтобы объект «работал нормально», но методы Dispose и finalize должны быть достаточно хорошо защищены, чтобы не повредить их . ] состояние других объектов, которое, в свою очередь, обычно требует использования либо блокировок, либо Interlocked операций с переменными состояния объекта.

4
ответ дан 28 November 2019 в 00:06
поделиться
Другие вопросы по тегам:

Похожие вопросы: