如何避免在C#中完成线程?

时间:2019-05-26 06:17:07

标签: c# multithreading rabbitmq

我有一个用C#编写的RabbitMQ客户端程序。当应用程序在基于控制台的应用程序中工作时(由于Console.ReadLine阻止了执行),在基于Windows窗体的应用程序中不起作用。在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 个答案:

答案 0 :(得分:2)

  1. 请勿使用using,因为那样会立即处理掉所有内容
  2. 在类字段而不是局部变量中存储所需的对象(连接,通道或消费者)
  3. 不需要线程,因为对象是异步处理事物的。只需创建对象就可以了
  4. 在应用程序终止或需要停止监听时关闭/处置对象

这样,它们将一直存在直到应用程序终止。

答案 1 :(得分:1)

如果您希望代码在两个平台上都可以工作,那么最好创建一个抽象层来公开消息并处理启动/停止逻辑。

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,以便正确处置资源:How do I extend a WinForm's Dispose method?

在控制台应用程序中,可能只是:

using (var manager = new RabbitMQManager())
{
    manager.MessageReceived += (sender, msg) => Console.WriteLine(msg);
    manager.Connect();
    Console.Read();
}

答案 2 :(得分:-2)

谢谢大家!根据您的所有建议,我找到了多种方法来实现这一目标。

我实现此目的的方法是将Factory,Connection和Channel设置为类变量,并在MainForm构造函数中定义它们。这样,对象得以保留,程序继续监听传入的消息。