创建c#windows服务以轮询数据库

时间:2013-10-30 18:29:26

标签: c# windows multithreading service timer

我想编写一个轮询数据库并根据被带回的数据执行操作的服务。

我不确定这样做的最佳方式是什么,我可以找到一些关于它的博客和这个堆栈溢出问题Polling Service - C#。但是我很担心他们都很老了,可能已经过时了。

任何人都可以告诉我目前的建议或最佳做法(如果有的话)做这样的事情,或者指点我最近的博客文章的方向。从我可以使用计时器或tpl任务收集的内容是两种可能的方法。

如果仍然建议定时器,那么当服务停止时它们将如何工作,因为我打算让这些服务执行的操作可能需要30多分钟,这就是为什么我说使用任务因为我可以使用任务取消令牌但是这些在取消时抛出异常(如果我错了就纠正我)并且我认为我不是真的想要这种行为(尽管如果你认为我有理由我会这样做的话,请纠正我)。

很抱歉,我可能会在一个问题上提出很多问题,但我并不完全确定自己在问什么。

2 个答案:

答案 0 :(得分:29)

使用Windows服务。使用计划任务本身并不是一个坏主意,但由于您说每两分钟可以进行一次民意调查,那么您可能最好使用该服务。该服务将允许您维持民意调查之间的状态,您也可以更好地控制民意调查的时间。你说这个操作一旦开始就可能需要30多分钟,所以你可能希望推迟民意调查直到操作完成。当逻辑作为服务运行时,这样做会容易一些。

最后,您使用什么机制来生成民意调查并不重要。您可以使用计时器或睡眠的专用线程/任务。就个人而言,我发现专用线程/任务比这些事情的计时器更容易使用,因为它更容易控制轮询间隔。此外,您绝对应该使用TPL提供的合作取消机制。它没有必要抛出异常。只有在您拨打ThrowIfCancellationRequested时才会这样做。您可以使用IsCancellationRequested来检查取消令牌的状态。

这是一个非常通用的模板,您可以用它来开始。

public class YourService : ServiceBase
{
  private CancellationTokenSource cts = new CancellationTokenSource();
  private Task mainTask = null;

  protected override void OnStart(string[] args)
  {
    mainTask = new Task(Poll, cts.Token, TaskCreationOptions.LongRunning);
    mainTask.Start();
  }

  protected override void OnStop()
  {
    cts.Cancel();
    mainTask.Wait();
  }

  private void Poll()
  {
    CancellationToken cancellation = cts.Token;
    TimeSpan interval = TimeSpan.Zero;
    while (!cancellation.WaitHandle.WaitOne(interval))
    {
      try 
      {
        // Put your code to poll here.
        // Occasionally check the cancellation state.
        if (cancellation.IsCancellationRequested)
        {
          break;
        }
        interval = WaitAfterSuccessInterval;
      }
      catch (Exception caught)
      {
        // Log the exception.
        interval = WaitAfterErrorInterval;
      }
    }
  }
}

就像我说的,我通常使用专用线程/任务而不是计时器。我这样做是因为我的轮询时间间隔几乎不变。如果检测到一个瞬态错误(如网络或服务器可用性问题),我通常会开始放慢民意调查的速度,因为我的日志文件不会一次又一次地快速连续地填充相同的错误消息。

答案 1 :(得分:3)

您有几个选择。从本质上最简单的选项开始,您可以决定将应用程序创建为控制台应用程序,并将可执行文件作为Windows任务计划程序中的任务运行。您需要做的就是将可执行文件指定为在任务中启动的程序,并让任务计划程序为您处理计时间隔。如果您不关心状态,这可能是首选方法,如果您不需要,则可以防止您不必担心创建和管理Windows服务。请参阅以下链接以了解如何使用调度程序。

Windows Task Scheduler

你可以做的另一种方法是创建一个Windows服务,并在该服务中使用一个计时器,特别是 System.Timers.Timer 。基本上,您可以将计时器间隔设置为您在运行流程之前要通过的时间量。然后你会注册定时器滴答事件,每次发生间隔时都会触发。在这种情况下,您基本上可以拥有您想要运行的流程;如果你愿意,这可以启动额外的线程。然后在初始设置之后,您只需调用定时器Start()函数或将Enabled属性设置为True以启动计时器。可以在描述该对象的MSDN页面上的示例中找到这种情况的一个很好的示例。有很多教程展示了如何设置Windows服务,所以我不打算专门进入那个。

MSDN: System.Timers.Timer

最后,更复杂的是设置一个侦听SqlDependency的Windows服务。如果在应用程序之外的数据库中发生了某些事情,但是您需要在应用程序或其他服务中了解它,此技术非常有用。以下链接提供了有关如何在应用程序中设置SqlDependency的良好教程。

Using SqlDependency To Monitor SQL Database Changes


我想从你的原始帖子中指出两件事,这些事情与你所提出的问题无关。

  1. 如果您正在编写真正的Windows服务,则不希望该服务停止。该服务应该不断运行,如果发生异常,应该妥善处理,不要停止服务。

  2. 取消令牌不必抛出异常;简单地不调用ThrowIfCancellationRequested()将导致不抛出异常,或者如果这是CancellationTokenSource,则在Cancel方法上将参数设置为false,然后检查令牌以查看是否在线程中请求取消并优先返回线程如果是的话。

  3. 例如:

        CancellationTokenSource cts = new CancellationTokenSource();
        ParallelOptions options = new ParallelOptions
        {
            CancellationToken = cts.Token
        };
        Parallel.ForEach(data, options, i =>
        {
            try
            {
                if (cts.IsCancellationRequested) return;
    
                //do stuff
    
            }
            catch (Exception ex)
            {
                cts.Cancel(false);
            }
        });