自定义命令 Windows 服务高优先级

3
我有一个工作追踪器WPF应用程序,部署在Windows Server 2008上,这个追踪器应用程序通过WCF服务与(追踪器)windows服务进行通信。
用户可以从Worker Tracker GUI应用程序创建任何工作条目/编辑/添加/删除/取消任何工作条目。内部它会向Windows服务发送一个请求。Windows服务将获取工作请求并在多线程中处理它。每个工作请求条目实际上会在输出文件夹位置创建N个工作文件(基于工作优先级)。
因此,每个工作请求都需要完成工作添加过程。
现在我的问题是,如果我取消当前正在创建的工作条目。我想在运行时停止当前的Windows服务工作。创建输出文件的当前线程应该停止。所有线程都应该被杀死。用户请求取消后,应该删除所有线程资源。
我的解决方法是:
我使用Windows Service On Custom Command方法在运行时向Windows服务发送自定义值。我在这里实现的是处理当前工作或当前线程(即为接收到的工作项创建输出文件),然后转到自定义命令以取消请求。
是否有任何方法使工作项请求在收到自定义命令后立即停止?
非常感谢任何解决方法。

你使用的是哪个版本的.NET?这将改变与你相关的答案。 - Andy Brown
1个回答

7

摘要

您主要是在谈论运行长时间任务的任务托管程序,并能够取消这些任务。您的具体问题似乎想知道在.NET中实现这一点的最佳方法。您的架构很好,尽管您勇于自己编写而不使用现有框架,但您没有提到以后如何扩展您的架构。

我更喜欢使用 TPL Task 对象。它支持取消操作,易于轮询进度等。您只能在 .NET 4 及以上版本中使用该对象。

在不基本设计整个作业托管引擎并了解您的 .NET 版本的情况下,提供代码是困难的。我已经详细描述了下面的步骤,并提供了示例代码的参考。

您使用 Windows 服务 OnCustomCommand 的方法很好,如果您的客户端和服务之间有这种选项进行通信,则还可以使用消息传递服务(请参见下文)。这更适用于许多客户端与中央作业服务交互并且作业服务不在与客户端相同的计算机上的情况。

在线程上运行和取消任务

在查看您的确切上下文之前,最好先回顾一下MSDN - 异步编程模式。有三种主要的.NET模式可以在线程上运行和取消作业,我按照使用偏好的顺序列出它们:

  • TAP: 任务异步模式
    • 基于Task,该模式仅自.NET 4以来可用
    • 从.NET 4开始运行和控制任何基于线程的活动的首选方式
    • 比EAP实现更简单
  • EAP: 事件异步模式
    • 如果您没有.NET 4或更高版本,则只有这个选择。
    • 难以实现,但一旦您理解了它,就可以轻松使用并且非常可靠
  • APM: 异步编程模型
    • 除非您维护遗留代码或使用旧的API,否则不再相关
    • 即使使用.NET 1.1,您也可以实现EAP的版本,因此我不会涵盖此内容,因为您说您正在实现自己的解决方案

架构

把这个想象成一个基于REST的服务。

  • 客户端提交作业,并获得作业标识符
  • 作业引擎在准备好时接收作业并开始运行
  • 如果客户端不再需要该作业,则使用其标识符删除该作业

这样,客户端完全隔离于作业引擎的工作之外,作业引擎可以随着时间的推移进行改进。

作业引擎

方法如下:

  • 为提交的任务生成通用标识符(UID),以便您可以:
    • 识别正在运行的任务
    • 轮询结果
    • 如果需要,取消任务
  • 将该UID返回给客户端
  • 使用该标识符排队作业
  • 当您有资源时
    • 通过创建任务来运行作业
    • 将任务存储在字典中,以UID作为键
当客户端需要结果时,他们会发送带有UID的请求,您通过检查从字典中检索到的任务来返回进度。如果任务完成,则可以发送请求获取已完成的数据,或者在您的情况下只需读取已完成的文件。
当他们想要取消时,他们会发送带有UID的请求,您将通过在字典中找到该任务并告诉它取消来取消任务。

作业内部的取消

在您的代码中,您需要定期检查取消令牌,以查看是否应停止运行代码(如果您使用TAP模式,请参见如何中止/取消TPL任务?,如果您使用EAP,请参见Albahari)。此时,您将退出作业处理,并且如果设计良好,您的代码应处置IDisposable,从内存中删除大字符串等。
取消的基本前提是检查取消令牌:
  • 在执行需要较长时间的工作块之后(例如调用外部API)
  • 在您控制的循环内(forforeachdowhile),您在每次迭代时检查
  • 在一长段可能需要“一些时间”的连续代码中,您插入定期检查点

您需要定义需要多快才能对取消做出反应——对于Windows服务,最好在毫秒内完成,以确保Windows不会在重新启动或停止服务时出现问题。

有些人使用线程来完成整个过程,并通过终止线程来实现——这很丑陋,不再推荐。

可靠性

您需要问自己:如果您的服务器重新启动、Windows服务崩溃或发生任何其他异常情况导致您丢失不完整的作业,会发生什么?在这种情况下,您可能需要一个可靠的队列架构,以便能够重新启动作业或重建尚未启动的作业队列。

如果您不想扩展规模,那么这很简单——使用本地数据库,其中Windows服务存储了作业信息。

  • 提交作业时,在数据库中记录其详细信息
  • 启动作业时,在数据库中针对该作业记录其状态
  • 当客户端收取作业时,在数据库中标记其为延迟垃圾回收,并在一定时间后(1小时、1天等)将其删除
  • 如果您的服务重新启动且有“正在进行中的作业”,则重新排队,然后再次启动作业引擎。

如果您确实想要扩展,或者您的客户端位于许多计算机上,并且您拥有一个由1个或多个服务器组成的作业引擎“农场”,那么请考虑使用消息队列而不是直接使用OnCustomCommand进行通信。

消息队列具有多种好处。它们将允许您可靠地向中央队列提交作业,许多工作人员随后可以选择并处理它们,并将您的客户端和服务器解耦以便您可以扩展作业运行服务。它们用于确保以高度解耦的方式可靠地提交和处理作业,这可以在本地或全球范围内工作,但始终是可靠的,您甚至可以将其与在云工作人员上运行的Windows服务相结合,从而可以动态扩展。

技术的例子包括 MSMQ(如果您想要维护自己的技术或必须留在自己的防火墙内),或者Windows Azure Service Bus (WASB) - 这是便宜的,并且已经为您完成。无论哪种情况,您都将想使用企业集成模式和最佳实践。对于 WASB 的情况,有许多 (MSDN)许多 (BrokeredMessaging 等 MSDN 示例)许多 (新的基于任务的 API) 开发人员资源,以及NuGet 包供您使用。


刚刚进行了一些小的编辑,没有改变内容但是希望能够更加清晰明了。 - Andy Brown

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