发送电子邮件后代码挂起,但电子邮件发送正常(静态异步任务)
Code hangs after sending email but email is sent OK (static async Task)
我正在编写一些 C# 代码来发送电子邮件(通过 Mailjet/Azure)。它确实发送了电子邮件,但由于某种原因,在单步执行代码时我从未通过这行代码....
MailjetResponse response = await client.PostAsync(request);
它只是在那个时候挂起。知道为什么吗?同样,邮件发送成功!
public static async Task<bool> SendEmailWithAttachment(string toAddress, string subject, string messageBody, bool sendBCCYesNo, bool sendFromInfoAddressYesNo, MemoryStream attachment = null, string attachmentFilename = null)
{
bool successYesNo = true;
try
{
MailjetClient client = new MailjetClient("xxxxxx", "xxxxx")
{
Version = ApiVersion.V3_1,
};
MailjetRequest request = new MailjetRequest
{
Resource = Send.Resource,
}
.Property(Send.Messages, new JArray {
new JObject {
{"From", new JObject {
{"Email", "xxxxx@xxxxx.com"},
{"Name", "xxxxx"}
}},
{"To", new JArray {
new JObject {
{"Email", toAddress},
{"Name", toAddress}
}
}},
{"Subject", subject},
{"TextPart", messageBody},
{"HTMLPart", messageBody}
}
});
MailjetResponse response = await client.PostAsync(request);
if (response.IsSuccessStatusCode) // I never get to this point
{
:
我正在使用这个调用代码....
if (Utility.SendEmailWithAttachment("xxxxx@xxxxx.com", "Test Email", "Test Body", false, false,
po, "AAA.pdf").Result == false)
{
lblStatus.Text = "Email send failure. Please contact support.";
return false;
}
有趣的是,当我 运行 示例 mailjet 提供的代码时,我的电子邮件发送正常并且我确实到达了 PostAsync 之后的行。据我所知,唯一的主要区别是我使用的是返回布尔值的任务,而不仅仅是任务。这是 mailjet 提供的工作正常的代码....
static void Main(string[] args)
{
RunAsync().Wait();
}
static async Task RunAsync()
{
MailjetClient client = new MailjetClient("xxxx", "xxxx")
{
Version = ApiVersion.V3_1,
};
MailjetRequest request = new MailjetRequest
{
Resource = Send.Resource,
}
.Property(Send.Messages, new JArray {
new JObject {
{"From", new JObject {
{"Email", "xxxx@xxxx.com"},
{"Name", "xxxx"}
}},
{"To", new JArray {
new JObject {
{"Email", "xxxx@xxxx.com"},
{"Name", "xxxx"}
}
}},
{"Subject", "Your email flight plan!"},
{"TextPart", "Dear passenger 1, welcome to Mailjet! May the delivery force be with you!"},
{"HTMLPart", "<h3>Dear passenger 1, welcome to <a href='https://www.mailjet.com/'>Mailjet</a>!</h3><br />May the delivery force be with you!"}
}
});
MailjetResponse response = await client.PostAsync(request);
if (response.IsSuccessStatusCode) // this line is reached!
{
提前致谢!
试试下面的代码。一个好的经验法则是不要使用 *.Result 除非先等待。
if ((await Utility.SendEmailWithAttachment("xxxxx@xxxxx.com", "Test Email", "Test Body", false, false,
po, "AAA.pdf")) == false)
{
lblStatus.Text = "Email send failure. Please contact support.";
return false;
}
方法中
public static async Task<bool> SendEmailWithAttachment(string toAddress, string subject, string messageBody, bool sendBCCYesNo, bool sendFromInfoAddressYesNo, MemoryStream attachment = null, string attachmentFilename = null)
更改以下行,来自:
MailjetResponse response = await client.PostAsync(request);
至:
MailjetResponse response = await client.PostAsync(request).ConfigureAwait(false);
请阅读这篇关于异步方法死锁的精彩article
问题是由对 .Result
的调用引起的 - 这是一个阻塞调用。如果在 UI 线程中调用它,它将阻塞线程。这意味着应用程序通常无法响应并出现冻结状态,这非常难看。
这也意味着任何 await
尝试在 UI 线程上恢复的调用,如
MailjetResponse response = await client.PostAsync(request);
不会。我再重复一遍 - 如果 await client.PostAsync(request);
似乎没有恢复,那是因为 某些东西 正在阻塞 UI 线程。
await
不会异步执行任何操作 运行,也不会启动任何线程。它 等待 已经 运行 宁非阻塞的异步操作。当这些完成后,它会在原始同步上下文中恢复——在桌面应用程序中,这意味着在 UI 线程上恢复。这就是允许 await
之后的任何代码修改 UI.
的原因
解决方案是删除 .Result
。没有它,无论如何使用异步调用都是毫无意义的——整个应用程序挂起,那么等待有什么意义?
假设在事件处理程序中调用该方法,事件处理程序本身应该变为异步。代码应该稍微清理一下。这不会影响异步行为,但会使其更易于阅读和调试:
private async void button1_Click(...)
{
var ok=await Utility.SendEmailWithAttachment("xxxxx@xxxxx.com", "Test Email", "Test Body",
false, false,po, "AAA.pdf");
if (!ok)
{
lblStatus.Text = "Email send failure. Please contact support.";
}
}
如果该方法不是事件处理程序,则应更改为异步方法。它的调用者也应该成为一个异步方法,一直到顶部 - 大多数时候,这是一个事件处理程序:
private async Task<bool> TrySend()
{
var ok=await Utility.SendEmailWithAttachment("xxxxx@xxxxx.com", "Test Email", "Test Body",
false, false,po, "AAA.pdf");
if (!ok)
{
lblStatus.Text = "Email send failure. Please contact support.";
return false;
}
else
{
.....
return true;
}
}
private async void button1_Click(...)
{
var ok=await TrySend();
...
}
SendEmailWithAttachment
本身不会尝试修改 UI,因此它 不需要 在 UI 线程上恢复。添加 ConfigureAwait(false)
将允许代码在线程池线程上恢复,并让 调用者 决定是否在 UI 上恢复。这主要是此时的优化,但是也去掉了原来死锁中的二次阻塞点。如果有人错误地添加回 .Result
,它会 "only" 冻结 UI :
MailjetResponse response = await client.PostAsync(request).ConfigureAwait(false);
我正在编写一些 C# 代码来发送电子邮件(通过 Mailjet/Azure)。它确实发送了电子邮件,但由于某种原因,在单步执行代码时我从未通过这行代码....
MailjetResponse response = await client.PostAsync(request);
它只是在那个时候挂起。知道为什么吗?同样,邮件发送成功!
public static async Task<bool> SendEmailWithAttachment(string toAddress, string subject, string messageBody, bool sendBCCYesNo, bool sendFromInfoAddressYesNo, MemoryStream attachment = null, string attachmentFilename = null)
{
bool successYesNo = true;
try
{
MailjetClient client = new MailjetClient("xxxxxx", "xxxxx")
{
Version = ApiVersion.V3_1,
};
MailjetRequest request = new MailjetRequest
{
Resource = Send.Resource,
}
.Property(Send.Messages, new JArray {
new JObject {
{"From", new JObject {
{"Email", "xxxxx@xxxxx.com"},
{"Name", "xxxxx"}
}},
{"To", new JArray {
new JObject {
{"Email", toAddress},
{"Name", toAddress}
}
}},
{"Subject", subject},
{"TextPart", messageBody},
{"HTMLPart", messageBody}
}
});
MailjetResponse response = await client.PostAsync(request);
if (response.IsSuccessStatusCode) // I never get to this point
{
:
我正在使用这个调用代码....
if (Utility.SendEmailWithAttachment("xxxxx@xxxxx.com", "Test Email", "Test Body", false, false,
po, "AAA.pdf").Result == false)
{
lblStatus.Text = "Email send failure. Please contact support.";
return false;
}
有趣的是,当我 运行 示例 mailjet 提供的代码时,我的电子邮件发送正常并且我确实到达了 PostAsync 之后的行。据我所知,唯一的主要区别是我使用的是返回布尔值的任务,而不仅仅是任务。这是 mailjet 提供的工作正常的代码....
static void Main(string[] args)
{
RunAsync().Wait();
}
static async Task RunAsync()
{
MailjetClient client = new MailjetClient("xxxx", "xxxx")
{
Version = ApiVersion.V3_1,
};
MailjetRequest request = new MailjetRequest
{
Resource = Send.Resource,
}
.Property(Send.Messages, new JArray {
new JObject {
{"From", new JObject {
{"Email", "xxxx@xxxx.com"},
{"Name", "xxxx"}
}},
{"To", new JArray {
new JObject {
{"Email", "xxxx@xxxx.com"},
{"Name", "xxxx"}
}
}},
{"Subject", "Your email flight plan!"},
{"TextPart", "Dear passenger 1, welcome to Mailjet! May the delivery force be with you!"},
{"HTMLPart", "<h3>Dear passenger 1, welcome to <a href='https://www.mailjet.com/'>Mailjet</a>!</h3><br />May the delivery force be with you!"}
}
});
MailjetResponse response = await client.PostAsync(request);
if (response.IsSuccessStatusCode) // this line is reached!
{
提前致谢!
试试下面的代码。一个好的经验法则是不要使用 *.Result 除非先等待。
if ((await Utility.SendEmailWithAttachment("xxxxx@xxxxx.com", "Test Email", "Test Body", false, false,
po, "AAA.pdf")) == false)
{
lblStatus.Text = "Email send failure. Please contact support.";
return false;
}
方法中
public static async Task<bool> SendEmailWithAttachment(string toAddress, string subject, string messageBody, bool sendBCCYesNo, bool sendFromInfoAddressYesNo, MemoryStream attachment = null, string attachmentFilename = null)
更改以下行,来自:
MailjetResponse response = await client.PostAsync(request);
至:
MailjetResponse response = await client.PostAsync(request).ConfigureAwait(false);
请阅读这篇关于异步方法死锁的精彩article
问题是由对 .Result
的调用引起的 - 这是一个阻塞调用。如果在 UI 线程中调用它,它将阻塞线程。这意味着应用程序通常无法响应并出现冻结状态,这非常难看。
这也意味着任何 await
尝试在 UI 线程上恢复的调用,如
MailjetResponse response = await client.PostAsync(request);
不会。我再重复一遍 - 如果 await client.PostAsync(request);
似乎没有恢复,那是因为 某些东西 正在阻塞 UI 线程。
await
不会异步执行任何操作 运行,也不会启动任何线程。它 等待 已经 运行 宁非阻塞的异步操作。当这些完成后,它会在原始同步上下文中恢复——在桌面应用程序中,这意味着在 UI 线程上恢复。这就是允许 await
之后的任何代码修改 UI.
解决方案是删除 .Result
。没有它,无论如何使用异步调用都是毫无意义的——整个应用程序挂起,那么等待有什么意义?
假设在事件处理程序中调用该方法,事件处理程序本身应该变为异步。代码应该稍微清理一下。这不会影响异步行为,但会使其更易于阅读和调试:
private async void button1_Click(...)
{
var ok=await Utility.SendEmailWithAttachment("xxxxx@xxxxx.com", "Test Email", "Test Body",
false, false,po, "AAA.pdf");
if (!ok)
{
lblStatus.Text = "Email send failure. Please contact support.";
}
}
如果该方法不是事件处理程序,则应更改为异步方法。它的调用者也应该成为一个异步方法,一直到顶部 - 大多数时候,这是一个事件处理程序:
private async Task<bool> TrySend()
{
var ok=await Utility.SendEmailWithAttachment("xxxxx@xxxxx.com", "Test Email", "Test Body",
false, false,po, "AAA.pdf");
if (!ok)
{
lblStatus.Text = "Email send failure. Please contact support.";
return false;
}
else
{
.....
return true;
}
}
private async void button1_Click(...)
{
var ok=await TrySend();
...
}
SendEmailWithAttachment
本身不会尝试修改 UI,因此它 不需要 在 UI 线程上恢复。添加 ConfigureAwait(false)
将允许代码在线程池线程上恢复,并让 调用者 决定是否在 UI 上恢复。这主要是此时的优化,但是也去掉了原来死锁中的二次阻塞点。如果有人错误地添加回 .Result
,它会 "only" 冻结 UI :
MailjetResponse response = await client.PostAsync(request).ConfigureAwait(false);