2010-02-10 2 views
14

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

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

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

ответ

11

В целом, если объект реализует 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

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

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(); 
} 

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

+0

Я полностью пропустил блок использования, просматривая этот код. Спасибо, что указали это. –

2

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

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

2

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

Поэтому я бы сказал, что это зависит от контекста того, как вы его используете - the 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; 
    } 
} 
+0

Это, по-видимому, пример, где .Close() не вызывается в ManualResetEvent и не используется блок. Я не думаю, что рабочий может закрыть его после набора, потому что основной поток использует его в вызове WaitHandle.WaitAll (manualEvents). –

+0

@ Kevin Моей точкой был массив WaitHandles, который не может быть заключен в предложение использования при создании как * я думаю * они будут закрыты, когда они придут, мне нужно будет проверить. –

17

Недавно я был отправлен отрывок из C# 4.0 in a Nutshell: The Definitive Reference Джозеф Альбахари, Бен Альбахар я. На стр. 834 в разделе Глава 21: Threading есть раздел, в котором говорится об этом.

Располагая Wait Ручки

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

Wait handles освобождается автоматически при поступлении заявки домен разгружается.

+0

Документация для WaitHandle.Finalize говорит, что в .NET 2.0 больше нет реализации. Вы также можете увидеть это с помощью декомпилятора. У WaitHandle больше нет финализатора. Я не знаю, почему, но любой заброшенный WaitHandle будет течь, кажется. См. Http://msdn.microsoft.com/en-us/library/vstudio/bb291974(v=vs.90).aspx – Djof

+4

@Djof: Несмотря на то, что 'WaitHandle' больше не имеет метода' Finalize', Думаю, это означает, что они течет. Вместо этого очистка обрабатывается в 'SafeHandle', к которой' WaitHandle' содержит ссылку. – supercat