2014-11-18 2 views
2

У меня есть служба Windows, которая обрабатывает приватную локальную очередь сообщений (MSMQ). Когда он запускается, он регистрирует обработчик события для PeekCompleted в очереди и затем вызывает асинхронный BeginPeek(), чтобы дождаться сообщения.Должен ли я позвонить в EndPeek после использования BeginPeek?

protected override void OnStart(string[] args) 
{ 
    if (String.IsNullOrWhiteSpace(args[0])) 
     return; 

    queue = new MessageQueue(args[0]); 
    queue.Formatter = new BinaryMessageFormatter(); 
    queue.PeekCompleted += new PeekCompletedEventHandler(OnPeekCompleted); 

    queue.BeginPeek(); 
} 

Как только сообщение прибывает, моя цель состоит в том, чтобы, очевидно, обработать это сообщение. Мой код в настоящее время имеет метод queue.Receive(), чтобы получить сообщение, содержащееся в транзакции, чтобы сообщение было возвращено в очередь в случае ошибок во время обработки. BeginPeek() вызывается снова, чтобы перезапустить цикл.

private static void OnPeekCompleted(Object source, PeekCompletedEventArgs asyncResult) 
{ 
    try 
    { 
     MessageQueue q = (MessageQueue)source; 

     using (MessageQueueTransaction trans = new MessageQueueTransaction()) 
     { 
      trans.Begin(); 

      Message msg = q.Receive(trans); 
      ProcessMessage(msg); 

      trans.Commit(); 
     } 

     // Restart the asynchronous peek operation. 
     q.BeginPeek(); 
    } 
    catch (MessageQueueException qEx) 
    { 
     // TODO: Walk it off. 
    } 
    return; 
} 

Do I, в любой точке, необходимо вызвать EndPeek() на очереди?

Возможно, чтобы избежать утечек памяти, например this question ссылается на? Я почти уверен, что мне этого не нужно, но документация не очень понятна. Он просто не чувствует себя на 100% право «начать» что-то без «окончания» это :)

Btw: Я мог бы заменить Receive() линии Message msg = q.EndPeek(asyncResult.AsyncResult), которая одинаково извлекает мне сообщение, но не удалить сообщение из очереди.

+0

Есть ли какие-либо убедительные * причины * для того, чтобы не вызывать '.EndPeek()'? Если вам не нужен результат '.EndPeek()', вы можете отказаться от него, но не называть его вообще - это почти наверняка плохая идея - вы, по сути, занимаетесь '.BeginPeek()' not выделяя любые неуправляемые ресурсы, которые '.EndPeek()' должен выпустить. Даже если это правда сейчас, кто говорит, что следующий выпуск не нарушит это предположение? –

+0

Боковое примечание: pleinolijf, ваше сообщение читается как «мой код выглядит некорректно, любой шанс, что он будет работать надежно». Лично я бы не делал этого независимо от возможности правильной работы кода - время, потраченное на поиск/исправление отсутствующего вызова EndXXXX всеми будущими читателями кода, скорее всего, выберет любую «экономию», которую вы сделаете. –

+0

Справедливые точки, вы оба. И это, по сути, то, о чем я спрашиваю: безопасно ли вы незывать EndPeek? Действительно ли BeginPeek выделяет ресурсы? Это то, что мне непонятно, хотя, очевидно, именование методов действительно предполагает это. Я ищу, это факты, а не предположения. – pleinolijf

ответ

10

Правильный ответ на этот вопрос требует определенных усилий, поскольку короткий ответ («нет») может вводить в заблуждение.

Является ли описание API явно скажем, вы должны позвонить по номеру EndPeek() за каждый звонок до BeginPeek()? Не в любой теме, я мог бы найти, и не только в том, что, как представляется, утверждать обратное here:

Чтобы использовать BeginPeek, создать обработчик событий, который обрабатывает результаты асинхронной операции, и связать его с вашим событие делегат. BeginPeek инициирует операцию асинхронного просмотра; MessageQueue уведомляется о получении сообщения PeekCompleted , когда сообщение поступает в очередь. MessageQueue может затем получить доступ к сообщению по телефону EndPeek(IAsyncResult)или путем получения результата с использованием PeekCompletedEventArgs.

(выделено мной). Это, кажется, говорят, что вы можете использовать либо .EndPeek() или просто непосредственно получить сообщение от арг событий без обязательств позвонить .EndPeek().

Хорошо, соответствует ли мандат на выполнение, который вы называете .EndPeek(), чтобы все работало правильно? По крайней мере для реализации System.Messaging в .NET 4.0 ответ отрицательный. Когда вы вызываете .BeginPeek(), асинхронная операция распределяется и обратный вызов регистрируется для завершения. Неуправляемые ресурсы, связанные с этой операцией, частично очищаются в этом обратном вызове, и только тогда вызываемый обработчик события. .EndPeek() фактически не выполняет никакой очистки - он просто ждет завершения операции, если он еще не установлен, проверяет наличие ошибок и возвращает сообщение. Так что действительно верно, что вы можете либо позвонить .EndPeek(), либо просто получить доступ к сообщению от событий args или ничего не делать - все будет работать так же плохо.

Плохо, да - обратите внимание, что я сказал «частично очищен». Реализация MessageQueue имеет проблему в том, что она выделяет ManualResetEvent для каждой асинхронной операции, но никогда не использует ее, оставляя это полностью до сборщика мусора - что-то, что разработчики .NET часто издеваются, но, конечно же, собственные разработчики Microsoft aren Совершенно. Я не тестировал, является ли утечка OverlappedData, описанная в this question, по-прежнему актуальной и не является очевидной из источника, но это меня не удивит.

В API есть другие предупреждающие знаки, что его реализация может оставить желать лучшие, наиболее заметно, что она не соответствует установленной .Begin...()/.End...() шаблона для асинхронных операций, но вводит обработчик событий в середине, создавая странный гибрид I» я никогда больше не увижу. Тогда есть очень сомнительное решение о наследовании класса Message от Component, что добавляет значительные накладные расходы для каждого экземпляра и ставит вопрос о том, когда и как его следует утилизировать ... в целом, а не в лучшей работе Microsoft.

Теперь, значит ли это, что вы не «обязаны» позвонить .EndPeek()? Да, в том смысле, что называть это или не называть это не имеет никакого функционального различия в отношении очистки или правильности ресурсов. Но со всем, что сказал, мой совет по-прежнему назовите его в любом случае. Зачем? Поскольку любой, кто знаком с тем, как работает асинхронный рабочий шаблон в других классах .NET, ожидает, что вызов будет там, и не помещать его там, выглядит как ошибка, которая может привести к утечке ресурсов. Если есть проблема с приложением, такой человек может разумно потратить некоторое бесплодное усилие на «проблемный код», которого нет. Учитывая, что звонок .EndPeek() имеет незначительные накладные расходы по сравнению с остальными машинами, я бы сказал, что сбережения в программиста удивляет больше, чем компенсирует затраты. Возможная альтернатива заключается в том, чтобы вставить комментарий вместо этого, объясняя, почему вы не звоните .EndPeek() - но, по всей вероятности, это все еще требует больше циклов программирования, чем просто называть его.

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

+0

+1 Отличная аргументация и благодарность за усилия;) – pleinolijf

Смежные вопросы