gen*_*ray 2 c# multithreading asynchronous producer-consumer system.threading.channels
我最近对我的框架进行了基准测试,发现它分配了大量垃圾。
我正在使用 andChannel<T>或TryRead操作ReadAsync在每次调用时分配内存。所以我用 a 交换了它,BlockingCollection<T>它也在 期间分配内存TryTake。
我使用了一个带有单个写入器/读取器的无界通道。还有一个正常的BlockingCollection<T>。
// Each thread runs this, jobmeta is justa struct
while (!token.IsCancellationRequested)
{
var jobMeta = await Reader.ReadAsync(token); // <- allocs here
jobMeta.Job.Execute();
jobMeta.JobHandle.Notify();
}
Run Code Online (Sandbox Code Playgroud)
探查器告诉我,所有分配都是由该ChannelReader.ReadAsync方法引起的。不幸的是,我无法显示完整的代码,但是由于我在热路径中使用它们,所以我需要不惜一切代价避免分配。
是否有任何替代方案在读/写/获取期间不分配内存并且行为相同(生产者/消费者多线程的并发类)?我怎样才能自己实现一个?
System.Threading.Channels库当前具有三个内置Channel<T>实现:
从这些实现来看,分配量越少BoundedChannel<T>。如果您不需要边界,可以使用 进行配置capacity: Int32.MaxValue。UnboundedChannel<T>内部基于 a ,这ConcurrentQueue<T>是一个非常高性能且无争议的集合(它是无锁的)。分配是无锁的必要妥协。该BoundedChannel<T>基于内部Deque<T>集合,该集合与locks 同步。仅当必须扩展其后备数组的容量时,它才会分配内存,这种情况在通道的生命周期中只会发生几次。
BlockingCollection<T>默认情况下也是基于a ,ConcurrentQueue<T>因此具有相同的优点和缺点。如果您想减少分配(同时降低性能并增加争用),您可以IProducerConsumerCollection<T>基于synchronized实现 an Queue<T>,并将其作为参数传递给BlockingCollection<T>构造函数。您可以使用这个答案作为起点。
CancellationToken最后,无论如何,将 s传递给任何这些 API 都会导致分配。必须CancellationToken注册回调才能立即生效,并且不进行分配的回调是不可能的。我的建议是摆脱CancellationToken,并找到其他一些优雅地完成的方式。就像使用 theChannelWriter<T>.Complete或 theBlockingCollection<T>.CompleteAdding方法一样。
| 归档时间: |
|
| 查看次数: |
1387 次 |
| 最近记录: |