Windows服务与计划任务的区别

125

在重复运行程序(例如,每两分钟一次)方面,使用Windows服务和计划任务的优缺点是什么?

11个回答

54

更新:

距我原来的答案已经近四年了,这个答案已经非常过时了。由于 TopShelf 的出现,Windows 服务的开发变得很容易。现在你只需要想办法支持故障转移就可以了...

原来的答案:

我真的不是 Windows 计划任务的粉丝。正如@moodforall 在上面指出的,必须提供用户密码,当有人更改该用户的密码时会很棘手。

Windows 计划任务的另一个主要问题是它以交互方式运行而不是作为后台进程运行。当每20分钟弹出15个 MS-DOS 窗口时,在 RDP 会话中,您会因为没有安装它们作为 Windows 服务而自责。

无论您选择什么,我都建议您将处理代码从控制台应用或 Windows 服务中分离出来。然后您可以选择,要么从控制台应用程序调用工作进程并将其连接到 Windows 计划任务,要么使用 Windows 服务。

您会发现计划 Windows 服务不是一件有趣的事情。一个相当常见的场景是您有一个长时间运行的过程,您想要定期运行该过程。但是,如果您正在处理队列,则实际上不希望两个相同的工作进程处理相同的队列。因此,您需要管理计时器,以确保如果您的长时间运行的过程运行时间超过分配的计时器间隔,则在现有进程完成之前不会再次启动。

在您编写了所有这些之后,您会想,为什么我不使用 Thread.Sleep 呢?它允许当前线程继续运行,直到它完成,然后暂停间隔开始,线程进入睡眠状态,并在所需时间后再次启动。很棒!

然后您会阅读互联网上的所有建议,很多专家告诉您这真是糟糕的编程实践:

http://msmvps.com/blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx

所以您会挠头并想:“WTF,撤消未检出内容->是,我确定->撤消今天所有的工作.....该死,该死,该死....”

但是,我很喜欢这个模式,即使每个人都认为它很糟糕:

单线程方法的 OnStart 方法。

protected override void OnStart (string args) {

   // Create worker thread; this will invoke the WorkerFunction
   // when we start it.
   // Since we use a separate worker thread, the main service
   // thread will return quickly, telling Windows that service has started
   ThreadStart st = new ThreadStart(WorkerFunction);
   workerThread = new Thread(st);

   // set flag to indicate worker thread is active
   serviceStarted = true;

   // start the thread
   workerThread.Start();
}

这段代码实例化了一个单独的线程,并将我们的工作函数附加到它上面。然后启动线程并让 OnStart 事件完成,这样 Windows 就不会认为服务已挂起。

单线程方法的工作方法。

/// <summary>
/// This function will do all the work
/// Once it is done with its tasks, it will be suspended for some time;
/// it will continue to repeat this until the service is stopped
/// </summary>
private void WorkerFunction() {

   // start an endless loop; loop will abort only when "serviceStarted"
   // flag = false
   while (serviceStarted) {

      // do something
      // exception handling omitted here for simplicity
      EventLog.WriteEntry("Service working",
         System.Diagnostics.EventLogEntryType.Information);

      // yield
      if (serviceStarted) {
         Thread.Sleep(new TimeSpan(0, interval, 0));
      }
   }

   // time to end the thread
   Thread.CurrentThread.Abort();
}

单线程方法的OnStop方法

protected override void OnStop() {

   // flag to tell the worker process to stop
   serviceStarted = false;

   // give it a little time to finish any pending work
   workerThread.Join(new TimeSpan(0,2,0));
}

来源:http://tutorials.csharp-online.net/Creating_a_.NET_Windows_Service%E2%80%94Alternative_1%3a_Use_a_Separate_Thread(链接已失效)

我多年来一直使用这种方式运行许多 Windows 服务,它对我很有效。我仍然没有看到人们认可的推荐模式。只要做适合自己的就可以了。


2
实际上,您可能希望使用自己的服务帐户运行服务。如果所有内容都以SERVICE或NETWORKSERVICE运行,则授予应用程序不必要的权限(这可能会危及不仅一个服务器而且整个网络的安全性)。 - Matthew Whited
4
你在第一段隐含地表示了另外一种情况。如果您没有使用内置账户之一,那么对于服务和任务计划程序而言,用户的密码是必须的。 - Nick Cox
1
@MatthewWhited NT AUTHORITY\NetworkService 是一个具有有限权限的账户。 - Ian Boyd
1
@IanBoyd 包括任何授予 NetworkService 相关其他应用程序的权限。 - Matthew Whited
1
如果您将项目设置为Windows应用程序而不是控制台应用程序,则根本不会看到MS-DOS窗口弹出。 - Orr
显示剩余2条评论

17
一些错误信息。Windows Scheduler可以在后台运行任务,而无需弹出窗口并且不需要密码。将其运行在NT AUTHORITY\SYSTEM帐户下。使用这个schtasks开关:

/ru SYSTEM

但是,为了访问网络资源,最好使用一个具有单独的不过期密码策略的服务帐户。编辑根据您的操作系统和任务本身的要求,您可能可以使用低于Localsystem特权的帐户和/ru选项。根据手册的细节。
/RU username

A value that specifies the user context under which the task runs. 
For the system account, valid values are "", "NT AUTHORITY\SYSTEM", or "SYSTEM". 
For Task Scheduler 2.0 tasks, "NT AUTHORITY\LOCALSERVICE", and 
"NT AUTHORITY\NETWORKSERVICE" are also valid values.

任务计划程序 2.0 可在 Vista 和 Server 2008 中使用。

在 XP 和 Server 2003 中,system 是唯一的选项。


2
请使用“本地服务”(又称“NT AUTHORITY\LocalService”)身份运行,而不是“LocalSystem”(又称“.\LocalSystem”)。前者权限受限,而后者则是管理员。 - Ian Boyd
1
是的,额外的账户 LocalServiceNetworkServiceschtasks v2 Vista 及以后版本中可用,并且应尽可能使用。当时,这是指 XP 和 Server 2003 中的 schtasks,其仅根据旧版本手册 http://technet.microsoft.com/en-us/library/bb490996.aspx 接受 System 作为参数。 - Amit Naidu
在XP上,您无法将计划任务作为SYSTEM运行。祝你好运。这篇文章总结得很好:https://cwl.cc/2011/12/run-scheduled-task-as-system.html - Pupsik

15

在.NET开发中,我通常从开发控制台应用程序开始,所有日志输出都将运行到控制台窗口。但是,只有在使用命令参数/console运行时,它才是一个纯粹的控制台应用程序。当没有这个参数时,它就像一个Windows服务,会在我自己编写的定时器上保持运行。

在我的看法中,Windows服务通常用于管理其他应用程序,而不是作为长时间运行的应用程序。或者,它们是像SQL Server、BizTalk、RPC连接、IIS这样的持续运行的重量级应用程序(尽管IIS技术上会将工作分配给其他进程)。

就我个人而言,针对重复性的维护任务和应用程序,例如文件复制/同步、批量邮件发送、文件删除或存档、数据校正(当没有其他解决方法时),我更喜欢使用定时任务而不是Windows服务。

在一个项目中,我参与开发了8或9个Windows服务,但是它们会闲置在内存中,每个实例会占用20MB或更多的内存。定时任务会完成它们的业务,并立即释放内存。


2
坦白地说,这是我听到的少数几个有意义的答案之一,但我认为在工作每两分钟启动的情况下,Windows服务会更好,因为启动进程并将其放入内存中,然后再将其删除将消耗大量资源。在这种情况下,牺牲20 MB的内存是更好的选择。 - Costa

13

启动和退出应用程序的开销有多大?每两分钟运行一次相当频繁。使用服务可能会比如此频繁地执行您的应用程序更加顺畅地让系统运行。

这两种解决方案都可以在用户未登录时运行程序,因此没有区别。但编写服务比普通桌面应用程序更为复杂 - 您可能需要一个单独的GUI客户端,通过TCP/IP、命名管道等与服务应用程序通信。

从用户的角度来看,我想知道哪个更容易控制。对于大多数非技术用户来说,服务和计划任务几乎是无法触及的,即他们甚至不会意识到它们的存在,并且可以进行配置/停止/重新安排等操作。


11
单词“服务”与“服务器”有一些共同之处。它们都需要始终运行和提供服务。任务只是一个任务。
角色扮演。如果我是另一个操作系统、应用程序或设备,并调用一个服务,我希望它正在运行并且可以响应。如果我(操作系统、应用程序、设备)只需要执行一个隔离的任务,那么我将执行一个任务,但如果我期望通信,可能是双向通信,我希望有一个服务。这与两个事物交流或想要执行单个任务的单个事物之间最有效的通信方式有关。
然后是调度方面。如果您希望在特定时间运行某些内容,请安排计划。如果您不知道何时需要它,或者需要它在“即兴发挥”的情况下,请使用服务。
我的回答更多是哲学性质的,因为这非常类似于人类相互交流和工作的方式。我们越了解沟通的艺术,“实体”了解其角色,做出决定就越容易。
撇开所有哲学,当您“快速原型制作”,正如我的IT部门经常做的那样,您必须尽力满足各种需求。一旦原型制作和概念验证完成,通常在早期规划和发现阶段,您必须决定长期可持续性更可靠的方式。
总之,这高度依赖于许多因素,但希望本文提供的见解能够给出启示而不是混淆。

4

Windows服务不需要任何人登录,Windows提供了停止、启动和记录服务结果的功能。

计划任务不需要您学习如何编写Windows服务。


9
预定任务也可以在用户未登录的情况下运行,但必须向计划代理提供用户密码。 - Marek Jedliński
1
除非帐户没有配置密码 (例如 NT AUTHORITY\LocalServiceNT AUTHORITY\NetworkService),否则任何提供的密码都将被忽略,因为这些帐户没有密码。 - Ian Boyd

4
  1. 具备正确权限的情况下,设置和锁定Windows服务更加容易。
  2. 服务更加“可见”,这意味着每个人(例如技术人员)都知道该去哪里查找。

5
你的第一点也适用于定期任务。 我不确定 "more visible" 是什么意思。定期任务和服务一样容易查看。 - w4g3n3r
@w4g3n3r: 大多数技术人员知道如何查看Windows服务以查看正在运行的内容。此外,如果它是服务(并且正在运行),它将显示在常规任务管理器列表中。很少有人使用计划任务。 - NotMe
此外,技术人员知道在出现问题时查看事件查看器。计划任务将该信息存储在文件系统上的日志文件中。我敢打赌大多数人甚至不知道该去哪里寻找它。 - NotMe
1
除非你有统计数据,否则我不同意“很少有人使用定时任务”的说法。我经常看到它们的存在。对于非编程人员需要每2分钟运行某些东西的情况,他们只需导航到计划任务(与访问服务的难度相同),但要创建一个新的计划任务,则需要使用向导。因此,他们只需要知道程序或脚本的位置以及其应该运行的频率。现在,如果你知道如何编码,并且将机器连接到网络上,需要保护登录信息,需要内置处理来防止多个实例(如果一个运行超过2分钟),那么我会选择服务。 - Gary
4
大多数技术人员知道如何查看Windows服务以了解正在运行的内容。这就是服务的一个问题;它们始终在运行 - 仅为了运行进程的簿记而消耗用户和内核资源。这就是为什么微软尽可能将多个服务合并为单个进程(svchost.exe)的原因;通过拥有进程来消除不必要的资源消耗。 - Ian Boyd
@IanBoyd:我认为合并到svchost中已经给技术人员识别问题带来了很多麻烦。 - NotMe

3

这是一个久远的问题,但我想分享一下我所面临的情况。

最近,我收到了一个需求,需要每隔10分钟从气象网站捕获雷达截图并保存在服务器中。

这要求我使用WebBrowser。 通常我会制作Windows服务,所以我决定把这个服务也做成服务,但它不断崩溃。 以下是我在事件查看器中看到的内容: 故障模块路径:C:\Windows\system32\MSHTML.dll

由于任务非常紧急,我没有多少时间进行研究和实验,所以我决定使用一个简单的控制台应用程序,并将其作为任务触发,顺利地执行了它。

我真的很喜欢Jon Galloway的文章,这是被Mark Ransom接受的答案推荐的。

最近,服务器上的密码在没有通知我的情况下更改了,所有的服务都无法执行,因为它们无法登录。 因此,在文章评论中声称这是一个问题的人。我认为Windows服务可能会面临相同的问题(如果我错了,请纠正我,我只是一个新手)

另外,提到的东西,如果使用任务计划程序,Windows弹出或控制台窗口弹出。 我从未遇到过这种情况。它可能会弹出,但至少非常瞬间。


我认为Jon Galloway的文章提出了一些好观点。此外,对于计划任务由于密码更改或其他原因无法执行的投诉,可以响应计划任务运行失败,以便您可以通知用户或执行其他操作。请参考以下解决方案:https://superuser.com/questions/249103/make-windows-task-scheduler-alert-me-on-fail#answer-249127 - Dan Csharpster

2

为什么不两者兼备呢?

过去我曾将“核心”部分放在库中,并在服务和控制台应用程序中包装调用Whatever.GoGoGo()。

如果每两分钟就要启动一次某些东西,很可能它并没有做太多事情(例如仅仅是一个“ping”类型的函数)。包装器不应该包含太多内容,只需一个方法调用和一些日志记录即可。


1
一般来说,核心信息是并且应该是代码本身必须能够从每个“触发器/客户端”执行。因此,从一种方法切换到另一种方法不应该很难。

过去,我们几乎总是使用Windows服务,但由于越来越多的客户逐步转向Azure,并且从将Console App(部署为计划任务)切换到Azure的WebJob比从Windows服务更容易,因此我们现在专注于计划任务。如果我们遇到限制,我们只需升级Windows服务项目并从那里调用相同的逻辑(只要客户正在OnPrem上运行..) :)

敬礼,y

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