邮件发送成功但C#程序执行await SendEmailAsync后停滞,如何解决?
你的程序在执行await client.SendEmailAsync(mail)后停滞不前,核心原因是同步等待异步方法引发的死锁,再加上代码里的一些潜在问题,导致后续流程完全无法执行。下面是具体的分析和修复步骤:
核心死锁原因
你在Main方法里用.Wait()同步等待AutoSendMessage这个异步方法,而await关键字会捕获当前的线程上下文。当SendEmailAsync完成后,await试图回到原来的上下文继续执行,但原来的线程已经被.Wait()阻塞,导致两个环节互相等待,程序直接卡死在这一步,自然不会执行后续代码。
另外,当前的异常处理逻辑在死锁场景下根本不会触发,因为方法根本没机会走到catch块,这也让你无法看到真正的问题所在。
具体修复步骤
1. 将Main方法改为异步(C# 7.1及以上支持)
控制台程序从C# 7.1开始支持异步Main方法,这是解决同步等待死锁的最优方案,彻底避免阻塞问题:
static async Task Main(string[] args) { // 保留你原来的初始化、循环逻辑... // 最后一行替换为异步等待,去掉.Wait() await program.AutoSendMessage(emailcontent); }
如果你的项目还没开启C# 7.1+,可以在项目属性的「Build」→「Advanced」里设置「Language version」为7.1或更高版本。
2. 完善异常处理,避免吞掉关键错误
当前的AutoSendMessage只打印异常消息,会丢失堆栈跟踪、内部异常等关键调试信息,修改后让错误更清晰:
private static async Task AutoSendMessage(string Message) { try { var client = new SendGridClient("API_KEY"); var from = new EmailAddress("xxxxxx", "xxxxxxx"); var subject = "Extra Information from API_Item Master(Item, ItemCode, Item Category) Program "; var messagedetail = Message; var to = new EmailAddress("xxxxxxxx", "xxxx"); var plainTextContent = "Hello, Email from the helper !" + messagedetail; var htmlContent = "<strong>Please check the Following Information:- </strong>" + messagedetail; var mail = MailHelper.CreateSingleEmail(from, to, subject, plainTextContent, htmlContent); var response = await client.SendEmailAsync(mail); // 新增:检查邮件发送响应,确认是否真的成功 if (!response.IsSuccessStatusCode) { var responseBody = await response.Body.ReadAsStringAsync(); throw new InvalidOperationException($"邮件发送失败,状态码:{response.StatusCode},响应内容:{responseBody}"); } } catch(Exception ex) { Console.WriteLine($"发送邮件出错:{ex.Message}"); Console.WriteLine($"错误堆栈:{ex.StackTrace}"); if (ex.InnerException != null) { Console.WriteLine($"内部异常:{ex.InnerException.Message}"); } // 如果需要上层处理这个异常,可以重新抛出 throw; } }
3. 修复数据库资源泄漏问题
你的代码里SqlCommand、SqlDataReader没有用using语句包裹,会导致数据库连接无法及时释放,引发资源泄漏。修改示例:
private DataTable GetExtraItemTypeFromTarget(string SourceDB, string targetDB) { DataTable dt = new DataTable(); string sql = "select t.ItemType ItemType from " + targetDB + ".dbo.ItemType t where " + "not exists(select * from " + SourceDB + ".dbo.ItemType s where t.ItemType = s.ItemType) "; // 使用using自动释放SqlCommand资源 using(SqlCommand ExtraItemTypeCommand = new SqlCommand(sql, dbConnection)){ ExtraItemTypeCommand.CommandTimeout = 180; // 使用using自动释放SqlDataReader资源 using(SqlDataReader ExtraItmeTypeReader = ExtraItemTypeCommand.ExecuteReader()){ dt.Load(ExtraItmeTypeReader); } } return dt; }
4. 规避SQL注入风险
当前SQL语句直接拼接targetDB参数,存在严重的SQL注入风险。建议对targetDB做白名单验证,确保传入的数据库名是合法的已知值,避免恶意输入破坏数据库。
修复后效果
修改完成后,程序会:
- 异步等待邮件发送完成,彻底解决死锁问题,后续流程正常执行
- 若邮件发送失败,会打印完整的错误信息,方便快速定位问题
- 数据库资源会被自动释放,避免连接泄漏
内容的提问来源于stack exchange,提问作者JJT

