Должен ли класс с членом Thread реализовывать IDisposable?

Допустим, у меня есть класс Logger, который регистрирует строки в низкоприоритетном рабочем потоке, который не является фоновым потоком. Строки ставятся в очередь в Logger.WriteLine и пережевываются в Logger.Worker. Никакие строки в очереди не могут быть потеряны. Примерно так (реализация, блокировка, синхронизация и т. д. опущены для ясности):

public class Logger
{
    private Thread workerThread;
    private Queue<String> logTexts;
    private AutoResetEvent logEvent;
    private AutoResetEvent stopEvent;

    // Locks the queue, adds the text to it and sets the log event.
    public void WriteLine(String text);

    // Sets the stop event without waiting for the thread to stop.
    public void AsyncStop();

    // Waits for any of the log event or stop event to be signalled.
    // If log event is set, it locks the queue, grabs the texts and logs them.
    // If stop event is set, it exits the function and the thread.
    private void Worker();
}

Поскольку рабочий поток является потоком переднего плана, я должен иметь возможность детерминистически остановить его, если процесс должен завершиться.

Вопрос. Является ли общей рекомендацией в этом сценарии позволить Logger реализовать IDisposable и остановить рабочий поток в Dispose()? Что-то вроде этого:

public class Logger : IDisposable
{
    ...

    public void Dispose()
    {
        AsyncStop();
        this.workerThread.Join();
    }
}

Или есть лучшие способы справиться с этим?


person Johann Gerell    schedule 22.12.2009    source источник


Ответы (4)


Это, безусловно, сработает — Thread квалифицируется как ресурс и т. д. Основное преимущество IDisposable исходит из оператора using, так что это действительно зависит от того, является ли типичным использованием владельца объекта использование объект в течение определенного времени в одном методе - т.е.

void Foo() {
    ...
    using(var obj = YourObject()) {
        ... some loop?
    }
    ...
}

Если это имеет смысл (возможно, рабочий насос), то ладно; IDisposable было бы полезно в случае возникновения исключения. Если это не является типичным использованием, то кроме выделения того, что оно нуждается в какой-то очистке, это не так уж полезно.

person Marc Gravell    schedule 22.12.2009

Обычно это лучший вариант, если у вас есть детерминированный способ размещения регистратора (использование блока в основной части приложения, попытка/наконец, обработчик выключения и т. д.).

person nitzmahone    schedule 22.12.2009

Может быть хорошей идеей, чтобы поток содержал WeakReference для управляющего объекта и периодически проверял, существует ли он. Теоретически вы можете использовать финализатор, чтобы подтолкнуть ваш поток (обратите внимание, что финализатор, в отличие от Dispose, не должен выполнять Thread.Join), но может быть хорошей идеей учесть возможность сбоя финализатора.

person supercat    schedule 15.03.2011

Вы должны знать, что если пользователь не вызывает Dispose вручную (используя или иным образом), приложение никогда не выйдет, поскольку объект Thread будет содержать сильную ссылку на ваш Logger. Ответ, предоставленный supercat, является гораздо лучшим общим решением этой проблемы.

person ghord    schedule 28.07.2011