conculenrtly处理rabbitmq消息

时间:2017-02-16 22:14:15

标签: c# multithreading rabbitmq

我问了一个问题here,关于为什么使用Thread.Run启动进程并没有像我预期的那样执行多少并发请求。

这个问题背后的原因是我试图创建一个类,它可以从rabbitmq队列中提取消息并同时处理它们,最多可以同时处理多个并发消息。

为此,我在Received类的EventingBasicConsumer处理程序中得到了以下结果。

async void Handle(EventArgs e) 
{
    await _semaphore.WaitAsync();

    var thread = new Thread(() =>
    {
        Process(e);
        _semaphore.Release(); 
        _channel.BasicAck(....);
    });
    thread.Start();
} 

然而,上一篇文章的评论不是为了启动一个线程,除非进行CPU绑定工作。

上述处理程序不知道工作是CPU绑定,网络,磁盘还是其他方式。 (Process是一种抽象方法)。

即便如此,我认为我必须在这里启动一个线程或任务,否则Process方法会阻塞rabbitmq线程,并且在完成之前不会再次调用事件处理程序。所以我一次只能处理一种方法。

在这里开始新的Thread好吗?最初我曾使用Task.Run,但这并没有产生那么多的工人。见其他文章。

FYI。通过在信号量上设置InitialCount来限制并发线程数。

1 个答案:

答案 0 :(得分:0)

正如在链接问题中已经说过的那样,大量的线程并不能保证性能,就好像它们的数量超过了逻辑内核的数量一样,你得到了thread starvation的情况而没有真正的工作

但是,如果您仍需要处理并发操作的数量,则可以尝试TPL Dataflow库,设置为MaxDegreeOfParallelism,例如this tutorial

var workerBlock = new ActionBlock<EventArgs>(
    // Process event
    e => Process(e),
    // Specify a maximum degree of parallelism.
    new ExecutionDataflowBlockOptions
    {
        MaxDegreeOfParallelism = InitialCount
    });
var bufferBlock = new BufferBlock();
// link the blocks for automatically propagading the messages
bufferBlock.LinkTo(workerBlock);

// asynchronously send the message
await bufferBlock.SendAsync(...);
// synchronously send the message
bufferBlock.Post(...);

BufferBlock是一个队列,因此将保留消息的顺序。此外,您可以添加不同的处理程序(具有不同的并行度),并使用过滤器lambda链接块:

bufferBlock.LinkTo(cpuWorkerBlock, e => e is CpuEventArgs);
bufferBlock.LinkTo(networkWorkerBlock, e => e is NetworkEventArgs);
bufferBlock.LinkTo(diskWorkerBlock, e => e is DiskEventArgs);

但在这种情况下,您应该在链的末尾设置一个默认处理程序,这样消息就不会消失(您可以使用NullTarget块):

bufferBlock.LinkTo(DataflowBlock.NullTarget<EventArgs>);

此外,该块可以是观察者,因此它们在UI端与Reactive Extensions完美配合。