Как избежать завершения потока в C #? - PullRequest
2 голосов
/ 26 мая 2019

У меня есть клиентская программа RabbitMQ, написанная на C #.Хотя приложение работает в консольном приложении (поскольку Console.ReadLine блокирует выполнение), оно не работает в приложении на основе Windows Form.В приложении Windows Form выполнение не ожидает Console.ReadLine и завершается по завершении.Я ищу решение, в котором мой слушатель отслеживает новые сообщения с сервера без прерывания.Вот код клиента:

    try {
            var factory = new ConnectionFactory() { HostName = "xxx" , UserName ="xxx", Password="xxx"};
            using(var connection = factory.CreateConnection())
            using(var channel = connection.CreateModel())
            {
                channel.ExchangeDeclare(exchange: "call_notify", type: "fanout");

                var queueName = channel.QueueDeclare().QueueName;
                channel.QueueBind(queue: queueName,
                                  exchange: "call_notify",
                                  routingKey: "");

                var consumer = new EventingBasicConsumer(channel);
                consumer.Received += (model, ea) =>
                {
                    var body = ea.Body;
                    var message = Encoding.UTF8.GetString(body);
                    Console.WriteLine(message);
                };
                channel.BasicConsume(queue: queueName,
                                     autoAck: true,
                                     consumer: consumer);

                Console.WriteLine(" Press [enter] to exit.");
                Console.ReadLine();  // Program does'nt wait here in windows form based application
            }
        }

Ответы [ 3 ]

2 голосов
/ 26 мая 2019
  1. Не используйте using, так как это сразу же утилизирует все
  2. Храните необходимые объекты (соединение? Канал? Потребитель?) В полях класса, а не в локальных переменных
  3. Нет необходимости в потоках, так как объекты обрабатывают вещи асинхронно.Просто создайте объекты и все.
  4. Закройте / утилизируйте объекты, когда приложение завершится, или вам нужно прекратить прослушивание

Таким образом, они будут жить, пока приложение не завершится.

1 голос
/ 26 мая 2019

Если вы хотите, чтобы код работал на обеих платформах, вам будет гораздо лучше создать слой абстракции, который отображает сообщения и имеет дело с логикой запуска / остановки.

public class RabbitMQManager : IDisposable
{
    private bool _disposed = false;
    private IModel _channel;
    private IConnection _connection;

    public event EventHandler<string> MessageReceived;

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposed)
        {
            if (disposing)
            {
                _channel?.Dispose();
                _connection?.Dispose();
            }

            _disposed = true;
        }
    }

    public void Dispose()
    {
        Dispose(true);
    }

    public void Connect()
    {
        var factory = new ConnectionFactory { HostName = "xxx", UserName = "xxx", Password = "xxx" };
        _connection = factory.CreateConnection();
        _channel = _connection.CreateModel();

        _channel.ExchangeDeclare(exchange: "call_notify", type: "fanout");

        string queueName = _channel.QueueDeclare().QueueName;
        _channel.QueueBind(queue: queueName,
                          exchange: "call_notify",
                          routingKey: "");

        var consumer = new EventingBasicConsumer(_channel);
        consumer.Received += (model, ea) =>
        {
            byte[] body = ea.Body;
            string message = Encoding.UTF8.GetString(body);
            MessageReceived?.Invoke(this, message);
        };

        _channel.BasicConsume(queue: queueName,
                             autoAck: true,
                             consumer: consumer);
    }
}

Затем вы можете использовать это в любом типе проекта, создавая экземпляр класса и подписавшись на событие MessageReceived. Например, реализация WinForms будет просто:

public class MyForm : Form
{
    private RabbitMQManager _rabbitMQManager;

    public MyForm() { _rabbitMQManager = new RabbitMQManager(); }

    // you can call this from constructor or some event
    public void Connect()
    {
        _rabbitMQManager.MessageReceived = (sender, msg) => someLabel.Text = msg;
        _rabbitMQManager.Connect();
    }
}

Посмотрите на эти вопросы и ответы, чтобы узнать, как переопределить MyForm.Dispose, чтобы ресурсы были правильно расположены: Как расширить метод Dispose для WinForm?

А в консольном приложении это может быть просто:

using (var manager = new RabbitMQManager())
{
    manager.MessageReceived += (sender, msg) => Console.WriteLine(msg);
    manager.Connect();
    Console.Read();
}
0 голосов
/ 26 мая 2019

Спасибо всем!Со всеми вашими предложениями я нашел несколько способов сделать это.

Способ, которым я реализовал это, сделал Factory, Connection и Channel переменными класса и определил их в конструкторе MainForm.Таким образом объекты сохраняются, и программа продолжает прослушивать входящие сообщения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...