我有一个网络表单,我使用TPL在后台发送电子邮件,因为我们的SMTP服务器很慢,很多用户最终都会因为沮丧而敲响提交按钮。在过去,我曾使用System.Threading
和静态方法来完成类似的任务 - 在.NET3.5中,我的代码看起来像这样:
Thread t = new Thread(new ParameterizedThreadStart(SendEmail));
t.Start(txtEmail.Text);
SendEmail的签名是public static void AddEmailToMailingListInBackground(object EmailString)
,我记得该方法必须是静态的,我必须将TextBox txtEmail的值传递给异步方法,否则可能会失去对控件值的访问权限作为页面生命周期继续独立。
现在使用System.Threading.Tasks
时,我的代码如下所示:
Task.Factory.StartNew(() => SendEmail(), TaskCreationOptions.LongRunning);
并且SendEmail的签名为private void SendEmail()
,我直接在该方法中访问Text
txtEmail
属性。
我有两个主要区别看这里。首先,我的异步方法不再是静态的。其次,如果我使用Threads,我可以在Page的生命周期完成后很长时间内访问方法中的Page的控制值。这两点让我相信,在所有任务完成或页面生命周期完成之前,页面一直处于活动状态,以先到者为准。我通过调试和逐步调试异步方法对此进行了测试 - 响应被发送到浏览器但我仍然可以单步执行并访问控件及其值。 This MSDN article有点帮助但仍然没有真正巩固我对TPL与.NET4之前执行异步调用的方式的理解。
任何人都可以告诉我,如果我的想法是正确的,这是在使用TPL与ASP.NET时可靠的行为?
有人愿意进一步阐述吗?
答案 0 :(得分:3)
从ASP.NET线程访问ASP.NET对象在技术上不是安全的。最好不要从页面/请求中提取SendMail需要的详细信息,并通过闭包传递它们。
此外,您需要确保“观察”SendMail中可能发生的任何异常,否则TPL将引发一个异常,从而导致整个Web应用程序崩溃。您可以使用TryMail调用本身的try / catch或使用TaskContinuationOptions.OnlyOnFaulted选项的另一个延续链上执行此操作。在延续方法中,您只需要访问前提的Exception属性,大概是为了记录它,以便TPL知道您“已经”观察到了异常。
所以把它们放在一起可能看起来像这样:
string userEmail = userEmailTextBox.Text;
// whatever else SendMail might need from the page/request
Task.Factory.StartNew(() => SendMail(userEmail))
.ContinueWith(sendEmailAntecedent =>
{
Trace.TraceError(sendEmailAntecedent.Exception.ToString());
},
TaskContinuationOptions.OnlyOnFaulted|TaskContinuationOptions.ExecuteSynchronously);