我问了一个问题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
来限制并发线程数。
答案 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完美配合。