导读:本期聚焦于小伙伴创作的《如何在.NET中实现两种定时发送邮件的代码实例》,敬请观看详情。系统需要在凌晨批量推送报表邮件,但不少团队仍靠人工点按钮。其实.NET自带多种机制可自动化此事。一种是用Timer组件在进程内按间隔触发,适合轻量服务;另一种是基于Quartz.NET调度框架,支持Cron表达式与持久化,适合复杂周期。下面通过两个完整示例展示如何配置SmtpClient发信、避免阻塞主线程,以及如何处理时区与异常重试,帮你在不同规模项目中选对方案。

在.NET应用程序里,定时发送邮件是常见的后台需求,比如每日对账通知、每周数据周报等。实现方式并不唯一,选择哪种取决于项目规模与可靠性要求。本文给出两种典型实现:基于System.Timers.Timer的进程内定时,以及基于Quartz.NET的调度框架方案。

如何在.NET中实现两种定时发送邮件的代码实例

一、使用System.Timers.Timer实现定时发邮件

Timer是.NET基础类库提供的轻量定时器,适合在Windows服务或长驻控制台程序中执行简单周期任务。它的Elapsed事件会在独立线程池线程上触发,因此不会阻塞主线程。需要注意的是,SmtpClient在.Net Framework中同步发送可能超时,建议用异步方法SendMailAsync。

下面示例演示每十分钟检查一次并发送测试邮件。我们在Elapsed事件里构造MailMessage,并设置SMTP服务器信息。为避免事件重入,用Interlocked做简单标记。

using System;
using System.Net;
using System.Net.Mail;
using System.Threading;

class TimerMailSender
{
    private static Timer _timer;
    private static int _running = 0;

    static void Main()
    {
        // 间隔十分钟
        _timer = new Timer(10 * 60 * 1000);
        _timer.Elapsed += async (s, e) =>
        {
            if (Interlocked.Exchange(ref _running, 1) == 1) return;
            try
            {
                using (var msg = new MailMessage())
                {
                    msg.From = new MailAddress("notify@ipipp.com");
                    msg.To.Add("user@ipipp.com");
                    msg.Subject = "定时邮件测试";
                    msg.Body = "这是Timer触发的邮件";
                    using (var client = new SmtpClient("smtp.ipipp.com", 25))
                    {
                        client.Credentials = new NetworkCredential("notify@ipipp.com", "pwd");
                        client.EnableSsl = true;
                        await client.SendMailAsync(msg);
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("发送失败:" + ex.Message);
            }
            finally
            {
                Interlocked.Exchange(ref _running, 0);
            }
        };
        _timer.AutoReset = true;
        _timer.Start();
        Console.WriteLine("按回车退出");
        Console.ReadLine();
    }
}

这种方案的优点是没有外部依赖,部署简单;缺点是如果进程重启,任务就中断,且不支持Cron这类复杂周期。对于要求不高的内部工具足够用。

另外要注意Timer的精度受系统时钟分辨率影响,不适合秒级严苛调度。若程序跑在IIS中,还要小心应用池回收导致定时器被销毁,此时应改用Windows任务计划或独立服务。

二、使用Quartz.NET实现更灵活的调度

当业务需要“每天9点”“每月1号”这类表达式周期,或要求任务持久化、集群不重复执行时,Quartz.NET是更专业的选择。它以内置调度器管理Job与Trigger,支持ADO.NET或内存存储。

下面代码定义邮件Job,并用CronScheduleBuilder配置每天上午9点触发。Quartz会自动从线程池取线程执行,我们同样用异步发信避免阻塞。

using System;
using System.Net;
using System.Net.Mail;
using System.Threading.Tasks;
using Quartz;

public class EmailJob : IJob
{
    public async Task Execute(IJobExecutionContext context)
    {
        using (var msg = new MailMessage())
        {
            msg.From = new MailAddress("notify@ipipp.com");
            msg.To.Add("user@ipipp.com");
            msg.Subject = "Quartz定时邮件";
            msg.Body = "由Quartz调度发出";
            using (var client = new SmtpClient("smtp.ipipp.com", 25))
            {
                client.Credentials = new NetworkCredential("notify@ipipp.com", "pwd");
                client.EnableSsl = true;
                await client.SendMailAsync(msg);
            }
        }
        await Task.CompletedTask;
    }
}

class QuartzDemo
{
    public static async Task Run()
    {
        var factory = new Quartz.Impl.StdSchedulerFactory();
        var scheduler = await factory.GetScheduler();
        await scheduler.Start();

        var job = JobBuilder.Create<EmailJob>()
            .WithIdentity("mailJob", "group1")
            .Build();

        var trigger = TriggerBuilder.Create()
            .WithIdentity("mailTrigger", "group1")
            .WithCronSchedule("0 0 9 * * ?") // 每天9点
            .Build();

        await scheduler.ScheduleJob(job, trigger);
    }
}

Quartz方案的强项在于调度语义清晰、可监控、可持久化。如果配了数据库存储,即使服务重启也能恢复触发器状态。它还支持 misfire 策略,处理服务宕机期间的漏发问题。

不过引入Quartz会增加包体积与学习成本,小项目未必划算。如果仅偶尔发信,第一种Timer方式更直观。实际选型应综合运维能力与业务严苛度。

三、两种方案对比与注意点

我们从部署、周期表达、可靠性三个维度比较。Timer胜在零依赖,Quartz胜在表达力与韧性。

维度Timer方案Quartz方案
外部依赖NuGet包Quartz
周期描述仅间隔毫秒Cron/日历
宕机恢复不支持可配置持久化

无论哪种方式,邮件发送都应加超时与重试。SmtpClient默认超时100秒,高并发时可调小。另外密码等敏感信息不要硬编码,应从配置或密钥库读取。

最后提醒,.NET Core之后SmtpClient虽仍可用,但官方标记为非弃用但建议评估第三方库如MailKit。若用MailKit,上述逻辑只需替换客户端类,定时器或调度器部分完全不变。

.NET定时任务邮件发送修改时间:2026-08-09 21:54:39

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。