异步进程结束后执行大规模后置处理的最佳实践及方案验证
异步事件处理器
我写了下面这个类用来异步执行可执行文件:
namespace Feather { internal class Helper { private static Process cmd; public static void RunLogic(string args) { cmd = new Process(); cmd.StartInfo.FileName = "Cotton.exe"; cmd.StartInfo.Arguments = args; cmd.EnableRaisingEvents = true; cmd.Exited += new EventHandler(cmd_Exited); } private static void cmd_Exited(object sender, EventArgs e) { // 进程已结束 cmd.Dispose(); // 可执行文件会生成一个输出文件 // 这个输出文件需要后置处理 // 后置处理代码量很大 // TODO: 在这里运行大量后置处理代码的最佳实践是什么? } } }
触发逻辑
我在另一个类里触发这个可执行文件的运行,代码如下:
namespace Feather { public class FeatherHollow : Command { protected override Result RunCommand(RhinoDoc doc, RunMode mode) { string args = ""; // 触发可执行文件的异步运行 Helper.RunLogic(args); // TODO: 当可执行文件生成输出文件后,如何运行大量后置处理代码? return Result.Success; } } }
问题
异步进程结束后,运行大规模后置处理代码的最佳实践和最简方式是什么?
delegate方案
这个方案是不是最佳选择?
namespace Feather { internal class Helper { public delegate void PostProcess(object sender, EventArgs e); public static void RunLogic(string args, PostProcess pp) { // ... cmd.Exited += new EventHandler(cmd_Exited); cmd.Exited += new EventHandler(pp); // ... } } }
调用类的代码如下:
namespace Feather { public class FeatherHollow : Command { protected override Result RunCommand(RhinoDoc doc, RunMode mode) { // ... Helper.RunLogic(args, PostProcess); // ... } private static void PostProcess(object sender, EventArgs e) { // 大量后置处理代码写在这里 } } }
方案分析与优化建议
你提出的delegate方案是可行的,但可以做些优化让代码更贴合.NET的通用写法:
- 复用内置委托类型
没必要自定义PostProcess委托,直接用.NET原生的EventHandler就够了,减少冗余代码:
namespace Feather { internal class Helper { private static Process cmd; public static void RunLogic(string args, EventHandler postProcessHandler) { cmd = new Process(); cmd.StartInfo.FileName = "Cotton.exe"; cmd.StartInfo.Arguments = args; cmd.EnableRaisingEvents = true; cmd.Exited += cmd_Exited; cmd.Exited += postProcessHandler; cmd.Start(); // 注意:原代码遗漏了启动进程的关键调用 } private static void cmd_Exited(object sender, EventArgs e) { cmd.Dispose(); } } }
调用端的写法和你原来的实现一致,不需要额外改动。
- 使用
async/await简化异步逻辑
如果你的项目基于.NET Framework 4.5+或者.NET Core/.NET 5+,更推荐用async/await来实现异步流程,代码线性化,可读性更强:
namespace Feather { internal class Helper { public static async Task RunLogicAsync(string args) { using var cmd = new Process(); cmd.StartInfo.FileName = "Cotton.exe"; cmd.StartInfo.Arguments = args; cmd.StartInfo.UseShellExecute = false; // 必须设置此属性才能使用WaitForExitAsync cmd.Start(); await cmd.WaitForExitAsync(); // 进程结束后可直接在这里处理,或者交由调用方执行后置逻辑 } } }
调用类的代码可以改成异步版本:
namespace Feather { public class FeatherHollow : Command { protected override async Task<Result> RunCommandAsync(RhinoDoc doc, RunMode mode) { string args = ""; await Helper.RunLogicAsync(args); // 直接在这里编写后置处理代码,逻辑更连贯 ExecutePostProcess(); return Result.Success; } private void ExecutePostProcess() { // 大规模后置处理代码 } } }
这种方式避免了事件订阅的繁琐,异步逻辑一目了然,更符合现代C#异步编程的最佳实践。
- 大规模后置处理的注意点
- 如果后置处理是CPU密集型任务,建议用
Task.Run放到后台线程执行,避免阻塞主线程(尤其是UI场景):private void ExecutePostProcess() { Task.Run(() => { // 耗时的后置处理操作 }); } - 确保后置处理中的资源访问是线程安全的,比如操作UI控件需要切换回主线程,共享数据要加锁或者使用线程安全容器。
总结
- 你的
delegate方案是合理的事件驱动实现,适合传统异步场景,优化后更简洁。 async/await方案更现代,代码可读性和可维护性更强,优先推荐使用。- 根据后置处理的类型(CPU密集/IO密集)选择合适的执行线程,保证程序性能和响应性。
内容的提问来源于stack exchange,提问作者Megidd
相关产品推荐
相关产品推荐

