在C#中实现函数非阻塞/异步的简单方法?
我有一个按钮,当用户点击它时,它会发送一封电子邮件。我希望这只是立即返回并在后台发送电子邮件,而不会在处理电子邮件时挂起UI。我搜索了在C#中实现函数非阻塞/异步的简单方法?,c#,.net,asynchronous,task-parallel-library,async-await,C#,.net,Asynchronous,Task Parallel Library,Async Await,我有一个按钮,当用户点击它时,它会发送一封电子邮件。我希望这只是立即返回并在后台发送电子邮件,而不会在处理电子邮件时挂起UI。我搜索了async/await/等等,发现了很多不同的方法——我正在寻找一个简单的解决方案。我的电子邮件代码: public void SendEmail(string toAddress, string subject, string body, string code = null) { try { var fromAddressObj =
async
/await
/等等,发现了很多不同的方法——我正在寻找一个简单的解决方案。我的电子邮件代码:
public void SendEmail(string toAddress, string subject, string body, string code = null) {
try {
var fromAddressObj = new MailAddress("noreply@me.com", "Name");
var toAddressObj = new MailAddress(toAddress, toAddress);
const string fromPassword = "Password";
var smtp = new SmtpClient {
Host = "smtp.office365.com",
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(fromAddressObj.Address, fromPassword)
};
using (var message = new MailMessage(fromAddressObj, toAddressObj) {
Subject = subject,
IsBodyHtml = true
}) {
message.Body = body;
smtp.Send(message);
}
} catch (Exception e) {
Elmah.ErrorSignal.FromCurrentContext().Raise(e);
}
}
如何修改此方法以使调用方不被阻止?您可以在任务中运行方法,而不必等待它:
private void button_Click(object sender, EventArgs e)
{
Task.Run( () => SendEMail(address, subject, body) );
}
SmtpClient
具有SendMailAsync
,因此使用它而不是Send
将在发送时取消阻止UI线程,但您仍然可以处理可能发生的任何异常:
private async void button_Click(object sender, EventArgs e)
{
await SendEmailAsync(address, subject, body)
}
public async Task SendEmailAsync(string toAddress, string subject, string body, string code = null)
{
try
{
var fromAddressObj = new MailAddress("noreply@me.com", "Name");
var toAddressObj = new MailAddress(toAddress, toAddress);
const string fromPassword = "Password";
var smtp = new SmtpClient {
Host = "smtp.office365.com",
Port = 587,
EnableSsl = true,
DeliveryMethod = SmtpDeliveryMethod.Network,
UseDefaultCredentials = false,
Credentials = new NetworkCredential(fromAddressObj.Address, fromPassword)
};
using (var message = new MailMessage(fromAddressObj, toAddressObj)
{
Subject = subject,
IsBodyHtml = true
})
{
message.Body = body;
await smtp.SendMailAsync(message);
}
}
catch (Exception e)
{
Elmah.ErrorSignal.FromCurrentContext().Raise(e);
}
}
如果async
方法的同步部分(wait之前的部分)仍然能够阻止UI线程,则可以将其卸载到线程池
线程:
private async void button_Click(object sender, EventArgs e)
{
await Task.Run(() => SendEmailAsync(address, subject, body))
}
注:
- 除非您处于无法使用
(UI事件处理程序不属于此类情况)的情况,否则不要在未等待的情况下启动wait
任务
仅适用于UI事件处理程序。不要在其他地方使用它async void
任务。当您有异步
版本的操作时,运行。这会浪费线程并限制可伸缩性。看看@SB2055,理想情况下,您还应该支持取消,这是SmtpClient.SendMailAsync不支持的。您可能想从中查看SendMailExImplAsync
。无需执行任务。运行
SmtpClient
具有自然异步的API。Task.Run
用于CPU限制的操作。这是一个IO绑定的操作。正如@YuvalItzchakov所提到的,SmtpClient
已经有了一个SendMailAsync
操作。虽然这样做有效,但这并不是最好的解决方案。你可以自己回答并建议一个版本,在这个版本中,你不阻塞用户界面,它可以立即继续吗?这里的另一个答案不满足OPs请求“呼叫者未被阻止”。它通过等待来阻止语句的进一步执行。这就是等待某事的意义。@nvoigt另一个答案不阻塞(这就是等待某事的意义)。请您解释一下为什么选择的答案是错误的,好吗?假设没有异步版本的操作,我仍然需要在不阻塞的情况下运行它,那么我显然不能使用wait,对吗?所以在某些情况下,使用任务而不等待似乎是可取的?为我的幼稚道歉,任何解释都会有帮助。@SB2055如果没有异步版本(async
或其他版本),那么Task.Run
将非常合适。但是您仍然有一个任务
到等待
,这就是任务返回的任务。你需要一个很好的理由不等待一项任务,而你的情况却没有。我觉得告诉OP他需要什么是很荒谬的。他决定他认为必要的事情。如果他不想等待功能,告诉他必须等待并不能回答他的问题。你的回答在技术上很好。。。但这并不是这个问题的答案。@nvoigt不,OP需要的是操作将是:“非阻止”和“在处理电子邮件时立即返回并在后台发送电子邮件,而不占用UI”。这只需使用异步等待
即可实现。没有理由不等待返回的任务,这样做是不好的。如果出于某种原因OP真的不能使用wait
,那么至少添加一个continuation,使用continuateWith
@nvoigt处理任何问题。这不是民主,你不应该删除你的答案,因为它被否决了,就像我没有删除我的答案,因为你的答案被否决了或者被否决了一样。如果你认为答案是正确的,你应该保留你的答案。当你意识到它错了时,你应该删除它。