Вопрос

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

private MessageQueue q;
private ManualResetEventSlim idle;

public void Start()
{
    idle = new ManualResetEventSlim();
    q.ReceiveCompleted += this.MessageQueue_ReceiveCompleted;    
    q.BeginReceive();
}    

public void Stop()
{ 
    this.q.Dispose();
    this.idle.Wait();    
}

private void MessageQueue_ReceiveCompleted(object sender, 
    ReceiveCompletedEventArgs e)
{
    Message inMsg;
    try
    {
        inMsg = e.Message;
    }
    catch (Exception ex)
    {
        this.idle.Set();
        return;
    }

    // Handle message

    this.q.BeginReceive();
}

Как мы надеемся, очевидно, метод Stop удаляет очередь сообщений, а затем ожидает установки дескриптора ожидания ожидания (что должно произойти, поскольку событие ReceiveCompleted будет вызываться при удалении, но свойство e.Message должно исключать).

Однако цикл сообщений просто продолжается!Я удалил очередь сообщений, но ей все равно удается прочитать ее, и обработчик исключений не вызывается, а это означает, что строка IDLE.Wait ждет вечно.

Насколько я понимаю, удаление очереди сообщений ДОЛЖНО завершить все ожидающие получения и вызвать событие, но e.Message (или q.EndReceive) должно выдать исключение.Разве это не так?Если нет, то как еще я могу безопасно выйти из цикла сообщений?

Спасибо

ОБНОВЛЯТЬ:

Вот полный пример (предполагается, что очередь существует)

class Program
{
    static MessageQueue mq;
    static ManualResetEventSlim idleWH;

    static void Main(string[] args)
    {
        idleWH = new ManualResetEventSlim();

        Console.WriteLine("Opening...");
        using (mq = new MessageQueue(@".\private$\test"))
        {
            mq.Formatter = new XmlMessageFormatter(new Type[] { typeof(int) });
            mq.ReceiveCompleted += mq_ReceiveCompleted;

            for (int i = 0; i < 10000; ++i)
                mq.Send(i);

            Console.WriteLine("Begin Receive...");
            mq.BeginReceive();

            Console.WriteLine("Press ENTER to exit loop");
            Console.ReadLine();

            Console.WriteLine("Closing...");

            mq.Close();
        }

        Console.WriteLine("Waiting...");
        idleWH.Wait();

        Console.WriteLine("Press ENTER (ex)");
        //Console.ReadLine();
    }

    static void mq_ReceiveCompleted(object sender, ReceiveCompletedEventArgs e)
    {
        try
        {
            var msg = mq.EndReceive(e.AsyncResult);
            Console.Title = msg.Body.ToString();

            // Receive next message
            mq.BeginReceive();
        }
        catch (Exception ex)
        {
            idleWH.Set();
            return;
        }
    }
}
Это было полезно?

Решение 2

Единственный способ заставить это работать — это очередь транзакций.Любая нетранзакционная очередь уязвима для этого.Это не ответ, но лучший совет, который я могу дать любому, кто это найдет.

Другие советы

Не совсем понимаю, как вы вообще это сделали.Ты должен вызовите MessageQueue.EndReceive() в событии.Только этот метод может вызвать исключение.Просмотрите пример кода MSDN для события ReceiveCompleted.И не перехватывайте Exception, это просто вызывает недиагностируемый сбой.Перехватите конкретное исключение, которое вы получаете при удалении очереди — ObjectDisposeException.

    private static volatile bool _shouldStop = false;

. . .

            _shouldStop = true;
            mq.Close();

. . .

        try
        {
            var msg = mq.EndReceive(e.AsyncResult);

            if ( _shouldStop)
            {
                idleWH.Set();
                return;
            }

            mq.BeginReceive();
        }

. . .

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top