Мне нужно вызвать Close() в ManualResetEvent?

Я читал в .NET Threading и работал над некоторым кодом, который использует ManualResetEvent. Я нашел множество образцов кода в Интернете. Однако при чтении документации для WaitHandle я увидел следующее:

WaitHandle реализует Dispose шаблон. См. Реализация Завершение и Утилизация для очистки неуправляемых Ресурсы.

Ни один из образцов, по-видимому, не вызывает .Close() для создаваемых ими объектов ManualResetEvent, даже приятную рекурсию и Concurrency статью блог pfxteam (Изменить) - у меня есть пробельный блок, который я пропустил). Это просто пример надзора или не нужен? Мне любопытно, потому что WaitHandle "инкапсулирует объекты, специфичные для операционной системы", поэтому может быть легко утечка ресурсов.

Ответ 1

В общем случае, если объект реализует IDisposable, он делает это по какой-либо причине, и вы должны называть Dispose (или Close, в зависимости от обстоятельств). В примере вашего сайта ManualResetEvent завернут внутри оператора using, который будет "автоматически" обрабатывать вызов Dispose. В этом случае Close является синонимом Dispose (что истинно в большинстве реализаций IDisposable, которые предоставляют метод Close).

Код из примера:

using (var mre = new ManualResetEvent(false))
{
   ...
}

расширяется до

var mre = new ManualResetEvent(false);
try
{
   ...
}
finally
{
   ((IDispoable)mre).Dispose();
}

Ответ 2

Недавно я отправил выдержку из С# 4.0 в двух словах: окончательная ссылка Джозеф Альбахари, Бен Альбахари. На странице 834 в разделе главы 21: Threading есть раздел, в котором говорится об этом.

Удаление дескрипторов ожидания

Как только вы закончите с ожиданием вы можете вызвать его метод Закрытьвыпустить операционную систему ресурс. Кроме того, вы можете просто отбросьте все ссылки на ожидание ручку и разрешить сборщик мусора сделать работу за вас когда-нибудь позже (механизмы ожидания реализуют удаление шаблон, по которому вызывает финализатор Закрыть). Это один из немногих сценарии, в которых эта резервная копия (возможно) приемлемо, потому что ждать ручки имеют легкую нагрузку на ОС (асинхронные делегаты полагаются на именно этот механизм для выпуска их IAsyncResults).

Выводятся команды ожидания автоматически, когда приложение выгрузки домена.

Ответ 4

Вы увидите код

 using (var mre = new ManualResetEvent(false))
 {
    // Process the left child asynchronously
    ThreadPool.QueueUserWorkItem(delegate
    {
        Process(tree.Left, action);
        mre.Set();
    });

    // Process current node and right child synchronously
    action(tree.Data);
    Process(tree.Right, action);

    // Wait for the left child
    mre.WaitOne();
}

использует ключевое слово 'using'. Это автоматически вызывает метод dispose при завершении, даже если код генерирует исключение.

Ответ 5

Я много использовал ManualResetEvent и не думаю, что когда-либо использовал его внутри одного метода - это всегда поле экземпляра класса. Поэтому using() часто не применяется.

Если у вас есть поле экземпляра класса, которое является экземпляром ManualResetEvent, сделайте реализацию класса IDisposable и в методе Dispose() ManualResetEvent.Close(). Затем во всех применениях вашего класса вам нужно использовать using() или сделать реализацию класса класса IDisposable и повторить и повторить...

Ответ 6

Если вы используете ManualResetEvent с анонимными методами, то это, очевидно, полезно. Но, как сказал Сэм, они часто могут быть переданы рабочим, а затем установлены и закрыты.

Итак, я бы сказал, что это зависит от контекста того, как вы его используете. образец кода MSDN WaitHandle.WaitAll() имеет хороший пример того, что я имею в виду.

Здесь пример, основанный на примере MSDN того, как создание WaitHandles с оператором using будет иметь исключение:

System.ObjectDisposedException
"Безопасная ручка закрыта"

const int threads = 25;

void ManualWaitHandle()
{
    ManualResetEvent[] manualEvents = new ManualResetEvent[threads];

    for (int i = 0; i < threads; i++)
    {
        using (ManualResetEvent manualResetEvent = new ManualResetEvent(false))
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(ManualWaitHandleThread), new FileState("filename", manualResetEvent));
            manualEvents[i] = manualResetEvent;
        }
    }

    WaitHandle.WaitAll(manualEvents);
}

void ManualWaitHandleThread(object state)
{
    FileState filestate = (FileState) state; 
    Thread.Sleep(100);
    filestate.ManualEvent.Set();
}

class FileState
{
    public string Filename { get;set; }
    public ManualResetEvent ManualEvent { get; set; }

    public FileState(string fileName, ManualResetEvent manualEvent)
    {
        Filename = fileName;
        ManualEvent = manualEvent;
    }
}