我在CPU密集型灯泡生产模拟中是否正确使用async/await?
异步await使用场景解析(结合灯泡生产模拟程序)
我用C#写了一个灯泡生产模拟程序:用户在命令行输入1,就启动异步函数模拟生产10000个灯泡,其中5%是次品;检测到次品时,观察者会收到通知并在控制台输出消息。我发现如果在prod.startMaschine(maschineID);前加await,程序就没法正常运行,所以去掉了这个关键字。想搞懂什么时候该用await,什么时候不该用,结合我的代码解释下。
Console.WriteLine("Bulb Production Monitoring!"); Main(); static async Task Main() { string input = ""; int maschineID = 1; Production prod = new Production(); Console.WriteLine("1: starts a production maschine"); Console.WriteLine("2: exits the production process"); while(!input.Equals("exit")) { input=Console.ReadLine(); switch(input) { case "1": prod.registerObserver(new Observer()); prod.startMaschine(maschineID); maschineID++; break; case "2" : input="exit"; break; default: break; } } } public class Bulb { public bool _functionTest; } public class Production { private List<Observer> _observerList = new List<Observer>(); public async Task startMaschine(int maschineId) { Console.WriteLine($"Maschine {maschineId} is ready for use ... "); int i=0; Random rndGenerator = new Random(); await Task.Run( async ()=> { while(i<10000) { int rndNumber=rndGenerator.Next(1,101); Bulb bulb = new Bulb(); await Task.Delay(2000); if (rndNumber<=5) { bulb._functionTest=false; this.notify(maschineId); } else { bulb._functionTest=true; } } } ); } public void registerObserver(Observer obs) { _observerList.Add(obs); } private void notify(int maschineId) { foreach(Observer obs in _observerList) { obs.update(maschineId); } } } public class Observer { public void update(int maschineId) { DateTime localDate = DateTime.Now; Console.WriteLine($"Defect bulb identified on maschine {maschineId} at {localDate}."); } }
为什么加await后程序“不正常”
你的startMaschine方法内部,用Task.Run启动了一个需要运行约5小时的任务(10000个灯泡×2秒/个)。当你在prod.startMaschine(maschineID);前加await时,主线程会暂停在这个位置,直到整个生产任务完全结束——这期间控制台会卡死,无法处理用户的任何新输入(比如再启动一台机器、输入2退出),所以你会觉得程序“无法正常运行”。
何时该用await
- 需要等待任务完成后再执行后续逻辑:比如你的需求是“启动一台机器,必须等它生产完所有灯泡才能启动下一台”,或者需要获取生产任务的结果(比如统计次品总数),这时候必须加
await,确保前序任务完成后再走下一步。 - 需要捕获异步任务的异常:如果异步任务可能抛出异常,不加
await的话,未处理的异常会在后台线程触发,可能直接导致程序崩溃;加await后可以用try-catch捕获并处理异常。
何时不该用await
- 需要任务后台运行,主线程继续处理其他工作:就像你的场景,启动生产机器后,希望用户还能继续操作命令行(启动新机器、退出程序),这时候不能加
await,让生产任务在后台线程独立执行即可。
你的代码优化建议
- Random实例问题:如果快速连续启动多台机器,每个
startMaschine里的Random会用当前时间作为种子,可能生成完全相同的随机数序列,导致次品率不符合预期。建议把Random设为Production类的静态成员:
private static Random _rndGenerator = new Random();
在startMaschine里直接用这个静态实例,避免重复初始化。
- 未处理的任务异常:不加
await时,startMaschine返回的任务如果抛出异常,会触发UnobservedTaskException,可能导致程序终止。可以在启动任务时添加异常处理:
_ = prod.startMaschine(maschineID).ContinueWith(task => { if (task.Exception != null) { Console.WriteLine($"机器{maschineID}生产出错:{task.Exception.Message}"); } }, TaskContinuationOptions.OnlyOnFaulted);
- Main方法调用问题:当前代码里直接调用
Main();,但Main是async Task类型,在C# 7.1及以上版本,async Main可以直接作为程序入口,编译器会自动处理,所以可以去掉开头的Main();调用。
内容的提问来源于stack exchange,提问作者Z.J
相关产品推荐
相关产品推荐

