如何异步执行慢进程并实时处理可用记录?优化PDF合并代码
优化PDF合并的异步方案
咱们得把远程文档获取和本地追加操作解耦——远程调用是IO密集型,可以并行异步跑,而本地的ThirdPartyComponent.AppendDocument是资源密集型,得串行处理避免资源竞争或性能下降。下面是具体的实现思路和代码示例:
1. 异步并行获取远程文档(控制并发)
远程请求可以并行发起,但要控制并发数,避免把远程服务器打崩。用SemaphoreSlim来限制同时发起的请求量,同时用**异步流(IAsyncEnumerable)**实现“拿到一个文档就返回一个”,不用等全部下载完再处理。
private async IAsyncEnumerable<Stream> FetchDocumentsAsyncEnumerable(List<string> selectedItems, int maxConcurrentRequests = 5) { var semaphore = new SemaphoreSlim(maxConcurrentRequests); var pendingTasks = new List<Task<Stream>>(); foreach (var item in selectedItems) { await semaphore.WaitAsync(); // 发起异步远程请求,完成后释放信号量 var fetchTask = Task.Run(async () => { try { return await ExternalService.GetDocument(item); } finally { semaphore.Release(); } }); pendingTasks.Add(fetchTask); // 一旦有任务完成,立刻返回结果 var completedTask = await Task.WhenAny(pendingTasks); pendingTasks.Remove(completedTask); yield return await completedTask; } // 处理剩余未完成的任务 foreach (var remainingTask in pendingTasks) { yield return await remainingTask; } }
2. 实时处理已获取的文档
拿到远程返回的文档流后,立刻串行执行本地追加操作(因为AppendDocument是资源密集型,多线程并行可能导致资源冲突或性能下降)。
完整合并逻辑示例:
public async Task MergePdfsAsync(List<string> selectedItems, string outputPath) { // 初始化基础合并文档 var mergedOutput = ThirdPartyComponent.CreateNewDocument(); try { // 异步并行拉取文档,实时处理每个返回结果 await foreach (var documentStream in FetchDocumentsAsyncEnumerable(selectedItems)) { using (documentStream) // 确保流被正确释放,避免内存泄漏 { if (documentStream != null) // 跳过获取失败的文档(可根据业务调整) { ThirdPartyComponent.AppendDocument(mergedOutput, documentStream); } } } // 保存最终合并后的PDF mergedOutput.Save(outputPath); } finally { // 释放基础文档资源 mergedOutput.Dispose(); } }
3. 额外优化建议
- 异常处理:在远程请求中加入异常捕获,记录日志并选择跳过失败的文档,避免整个合并流程中断;
- 并发数调整:根据远程服务器的限流规则,调整
maxConcurrentRequests的值,平衡速度和稳定性; - 资源监控:如果本地
AppendDocument占用大量CPU/内存,可以考虑在处理时加入短暂延迟,避免系统资源耗尽。
内容的提问来源于stack exchange,提问作者Curtis
相关产品推荐
相关产品推荐

