Нужно ли вызывать Close () для ManualResetEvent? - PullRequest
15 голосов
/ 10 февраля 2010

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

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

Кажется, что ни один из примеров не вызывает .Close () для создаваемых ими объектов ManualResetEvent, даже прекрасную Рекурсию и параллелизм статью из блога pfxteam (* 1016) * Edit - здесь есть блок использования, который я пропустил ). Это просто пример недосмотра или не нужен? Мне любопытно, потому что WaitHandle «инкапсулирует объекты, специфичные для операционной системы», поэтому может легко произойти утечка ресурсов.

Ответы [ 6 ]

21 голосов
/ 01 марта 2010

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

Утилизация ручек ожидания

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

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

11 голосов
/ 10 февраля 2010

Как правило, если объект реализует 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 голосов
/ 10 февраля 2010

Если вы используете 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;
    }
}
2 голосов
/ 10 февраля 2010

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

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

2 голосов
/ 10 февраля 2010

Вы заметите, что код

 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 после завершения, даже если код выдает исключение.

2 голосов
/ 10 февраля 2010

Закрытие обрабатывается внутри Dispose для ManualResetEvent, и вызывается оператором using.

http://msdn.microsoft.com/en-us/library/yh598w02%28VS.100%29.aspx

...