在Windows服务中的线程中使用计时器

4

我无法确定如何以最佳方式解决这个问题。

现在,我有一个Windows服务,其唯一任务是从具有特定DSN的数据库中收集数据,然后如果数据有效,则发送电子邮件。该服务包含一个计时器,每5分钟触发一次,并执行上述任务。

现在我需要重新编写Windows服务,以便能够在多个DSN上运行。 我想在Windows服务内部创建几个线程,然后再在每个线程内部设置一个单独的计时器。 这是一个好主意吗?如何实现?我想避免为每个DSN都拥有一个Windows服务。

如果我讲不清楚,我会试着画图。

                             Windows Service

线程1(DSN1)-----------------------------线程2(DSN2)----------------------线程3(DSN3)
计时器(每X分钟触发)-----------------计时器(相同)-------------------------计时器(相同)
逻辑()---------------------------------------------逻辑---------------------------------逻辑()

希望我的问题有意义 :)

4个回答

6
据我所知,每个计时器都代表着一个独立的线程。有了这个认识,我会尝试为每个给定的DSN动态创建计时器对象。
public partial class Service1 : ServiceBase
{
    public Service1()
    {
        InitializeComponent();
    }

    private List<GetDataFromDSN> list = null;
    protected override void OnStart(string[] args)
    {
        list = new List<GetDataFromDSN>();
        // assume args contains your given dsn values
        foreach (string dsn in args)
        {
            GetDataFromDSN newObj = new GetDataFromDSN();
            newObj.DSN = dsn;
            list.Add(newObj);
            newObj.Start();
        }
    }
}

public class GetDataFromDSN
{
    public string DSN { get; set; }
    private Timer timer = null;
    private double interval = 1000*60*5; // 5 minutes interval
    public GetDataFromDSN()
    {
        // init your object
        timer = new Timer(interval);
        timer.Elapsed +=new ElapsedEventHandler(timer_Elapsed);
    }
    private void timer_Elapsed(object sender, ElapsedEventArgs e)
    {
        // do what ever you want
    }
    public void Start() // or even make timer public
    {
        timer.Start();
    }
    public void Stop()
    {
        timer.Stop();
    }
}

+1. 在这种情况下,甚至没有必要手动开启线程,这毫无意义。它们是用来等待什么的?计时器会按需获取线程。 - TomTom
感谢您的反馈,我之前不知道计时器本身就代表了一个线程。那么我是不是可以完全省略线程部分呢?BackgroundWorker 也使用线程,那我为什么还要使用它呢?另一个问题是,当我停止服务时如何关闭线程,但如果我不需要线程而只需要计时器,这就不再重要了吗? - mRf
是的,你应该完全放弃线程部分!你可以选择使用BackgroundWorker - 在WinForms上使用BW有一些优点,但在服务中,你可以使用计时器。 - Pilgerstorfer Franz
感谢提供的解决方案,非常好用 :) 有使用此计时器而不是 Threading.Timer 的原因吗? - mRf
Timers.Timer比Threading.Timer更容易使用。但是Threading.Timer允许更多的灵活性。 - Pilgerstorfer Franz
显示剩余2条评论

4

每个DSN都需要在不同的线程上吗?

如果您将电子邮件检索和验证逻辑封装在一种服务中,线程调用该服务,则可以隐藏存在多个DSN的事实。例如,一个 IEmailService 可能具有以下约定:

public interface IEmailService
{
    void SendEmailsToValidAddresses();
}

实现可能看起来像这样:

public class MultipleSourcesEmailService : IEmailService
{
    private IEnumerable<IDatabaseSource> databases;
    public EmailService(params IDatabaseSource[] sources)
    {
        databases = new List<IDatabaseSource>(sources);        
    }

    public void SendEmailsToValidAddresses()
    {
        foreach(var database in databases)
        {
            var emailAddresses = database.SelectAllEmailAddresses();
            ValidateAndSendEmailsTo(emailAddresses);
        }
    }

    public void ValidateAndSendEmailsTo(IEnumerable<string> emailAddresses)
    {
        // Perform appropriate logic
        ...
    }

}        

以此方法,您的计时器逻辑可以保持不变,并在单个线程上运行,而发送电子邮件的问题则分离到了中。这也意味着,您可以实现一个和一个,并在完整代码时交换多个源,在使用服务的消费者永远不需要知道。
当然,像上面实现的EmailService将顺序地从多个来源发送电子邮件-如果您需要它并行运行,则可以更改EmailService以启动每个DSN的新线程,您甚至可以称其为:,但作为IEmailService的消费者,您的调度永远不会知道差异。

1

0

尝试使用System.Threading.Timer

这是我项目中的示例代码,希望能有所帮助。

public void StartDSNTimers()
    {
        _tmr1 = new Timer(CheckMessages, dsn1, 0, 60000);
        _tmr2 = new Timer(CheckMessages, dsn2, 0, 60000);
        _tmr3 = new Timer(CheckMessages, dsn3, 0, 60000);
    }

    private void CheckMessages(object obj)
    {
        //Logic
    }

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