如何在TransformBlock后向对象写入数据?TPL数据流使用疑问
嘿,刚接触TPL20分钟就敢上手TransformBlock,已经超棒啦!咱们来一步步解决你的问题~
首先得搞清楚为什么Parallel.ForEach不适合你的场景:当你给它传一个async lambda的时候,这个lambda会被当作async void方法来处理——Parallel.ForEach根本没法追踪这些异步任务的完成状态,它只会把任务丢出去就继续往下跑,完全不管你的API请求有没有做完,这肯定不是你想要的结果。
再说说你用TransformBlock时踩的坑:Post()方法返回的是个bool,它只是告诉你“这个消息有没有成功放进块里”,不是API请求返回的List<Something>,所以直接赋值给r.SomeList肯定会报类型错误。
那该怎么正确实现呢?给你两种方案,按需选择:
方案一:用Task.WhenAll(最适合你的简单场景)
这是异步并行处理最直接的方式,逻辑清晰还不用搞复杂的数据流块:
// 先给每个结果对象创建对应的异步任务,任务里完成API调用并赋值 var tasks = results.Select(async r => { r.SomeList = await apiHelper.Get(r.Id); }).ToList(); // 等待所有异步任务全部完成 await Task.WhenAll(tasks);
这个方法会同时发起所有API请求(底层会受系统连接数限制,不会真的无限制并行),然后等待所有请求都返回并完成赋值,完美符合你的需求。
方案二:正确使用TransformBlock(适合复杂数据流场景)
如果你之后有更复杂的处理流程(比如还要对结果做后续加工、需要严格控制并发数),可以用TransformBlock,但要调整写法,把结果和原对象关联起来:
// 这里假设你的结果对象类型是ResultType,替换成你实际的类型 var getBlock = new TransformBlock<ResultType, ResultType>(async r => { // 直接在原对象上赋值 r.SomeList = await apiHelper.Get(r.Id); return r; }, new ExecutionDataflowBlockOptions { // 可以设置具体的并发数,比如10,避免一次性发太多API请求 MaxDegreeOfParallelism = 10 }); // 创建一个接收处理后对象的块,用来等待所有任务完成 var bufferBlock = new BufferBlock<ResultType>(); // 把处理块和接收块链接起来,并且让接收块继承处理块的完成状态 getBlock.LinkTo(bufferBlock, new DataflowLinkOptions { PropagateCompletion = true }); // 把所有结果对象发送到处理块里,用SendAsync比Post更可靠(会等待块有空间再发送) foreach (var r in results) { await getBlock.SendAsync(r); } // 告诉处理块:我已经发完所有消息了,别再等新的了 getBlock.Complete(); // 等待所有处理流程全部完成 await bufferBlock.Completion;
这样每个结果对象都会被正确处理,你也能通过MaxDegreeOfParallelism精准控制并发请求的数量,避免压垮API服务器。
内容的提问来源于stack exchange,提问作者90abyss
相关产品推荐
相关产品推荐

