如何异步调用一个方法

5

我尝试遵循这个链接异步调用,但是一些类已经过时了。
因此,我想要一个确切的答案来完成我的项目。

public class RegisterInfo
{
    public bool Register(UserInfo info)
    {
        try
        {
            using (mydatabase db = new mydatabase())
            {
                userinfotable uinfo = new userinfotable();
                uinfo.Name = info.Name;
                uinfo.Age = info.Age;
                uinfo.Address = info.Address;

                db.userinfotables.AddObject(uinfo);
                db.SaveChanges();

                // Should be called asynchronously
                Utility.SendEmail(info); // this tooks 5 to 10 seconds or more.

                return true;
            }
        }
        catch { return false; }
    }
} 

public class UserInfo
{
    public UserInfo() { }

    public string Name { get; set; }
    public int Age { get; set; }
    public string Address { get; set; }
}  

public class Utility
{
    public static bool SendEmail(UserInfo info)
    {
        MailMessage compose = SomeClassThatComposeMessage(info);
        return SendEmail(compose);
    }

    private static bool SendEmail(MailMessage mail)
    {
        try
        {
            SmtpClient client = new SmtpClient();
            client.Host = "smtp.something.com";
            client.Port = 123;
            client.Credentials = new System.Net.NetworkCredential("username@domainserver.com", "password");
            client.EnableSsl = true;

            ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);
            client.Send(mail);

            return true;
        }
        catch { return false; }
    }
}    

请查看Register方法。 保存数据后,我不希望等待邮件的发送。如果可能的话,我想在其他线程上处理邮件的发送,以便用户不必等待更长的时间。
我不需要知道邮件是否成功发送。
希望您能理解我的意思。对于我的糟糕英语表示抱歉。

你的代码有一个 bug,当 SendEmail 函数抛出异常时,它会返回 false,但在 Register 函数中它仍然会返回 true。这是假设 bool 表示成功/失败。 - cubski
谢谢关心,但这不是原始代码。我只想知道如何进行异步调用。 - fiberOptics
可能是重复的问题:如何在C#中异步调用任何方法 - nawfal
3个回答

14

使用 Thread

new Thread(() => Utility.SendEmail(info)).Start();

使用ThreadPool

ThreadPool.QueueUserWorkItem(s => Utility.SendEmail(info));

使用Task

Task.Factory.StartNew(() => Utility.SendEmail(info));

当然,ThreadThreadPool需要使用System.Threading,而Task则需要使用System.Threading.Tasks


正如David Anderson所述,SmtpClient已经支持异步发送 (我可能应该注意函数的内容而不是回答问题),因此从技术上讲,您可以使用它来处理发送,尽管它不会卸载整个方法的处理。


第一次看到这个使用lambda表达式。花了几秒钟才弄清楚它是如何工作的。+1 - Ramesh
1
@DavidAnderson - 感激不尽。实际上我以前在MVC中使用过它,并取得了很大的成功,所以听到它不被推荐我感到惊讶。 - M.Babcock
额,你得留意一下,今天已经过得相当漫长了。使用 System.Net.Mail.SmtpClient 没有任何问题。System.Web.Mail.SmtpMail 已经过时了,你应该使用 *.Net.Mail.SmtpClient。(请阅读我的更新帖子) - David Anderson

3

SmtpClient已经有一个SendAsync方法,您不需要编写自己的异步代码来完成此操作。

@关于SmtpClient在ASP.NET中不能直接使用的评论:
这绝对是不正确的。它非常好用,也是建议使用的API。但是您必须理解ASP.NET页面生命周期以及服务器上线程行为。否则就没有理由不使用它。


从您的链接中,我看到它无法与ASP.NET开箱即用地正常工作。只是想强调这一点 http://blog.jdconley.com/2009/01/fire-and-forget-email-webservices-and.html - Ramesh
您需要设置页面以允许异步方法,并确保正确遵循页面生命周期。否则,使用SendAsync没有任何问题。该博客讨论了负载问题,但如果设计良好并且为页面的预期负载提供适当的硬件,则不会出现此问题。 - David Anderson

2

或者使用Async CTP(异步编程组件)

public async Task<bool> Register(UserInfo info)
{
    try
    {
        using (mydatabase db = new mydatabase())
        {
            userinfotable uinfo = new userinfotable();
            uinfo.Name = info.Name;
            uinfo.Age = info.Age;
            uinfo.Address = info.Address;

            db.userinfotables.AddObject(uinfo);
            db.SaveChanges();

            //Wait for task to finish asynchronously
            await Utility.SendEmail(info);

            return true;
        }
    }
    catch { return false; }
}

private Task SendEmail(MailMessage mail)
{
    SmtpClient client = new SmtpClient();
    client.Host = "smtp.something.com";
    client.Port = 123;
    client.Credentials = new System.Net.NetworkCredential("username@domainserver.com", "password");
    client.EnableSsl = true;

    ServicePointManager.ServerCertificateValidationCallback = new RemoteCertificateValidationCallback(ValidateServerCertificate);

    //The smtp SendTaskAsync is an extension method when using Async CTP
    return client.SendTaskAsync("from", "recipients", "subject", "body");
}

您的原始代码中也存在一个错误。当SendEmail函数内部抛出异常时,它会返回false,但在register函数内部它仍将返回true。假设bool表示成功或失败。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接