.NET:同步长时间运行任务的机制

Posted

技术标签:

【中文标题】.NET:同步长时间运行任务的机制【英文标题】:.NET: Mechanism for sync-ing long-running tasks 【发布时间】:2011-01-19 02:23:37 【问题描述】:

问题描述:您编写了一个库,其中包含一些可能需要很长时间才能完成的算法/任务,原因有很多:计算、文件系统、网络通信等。您希望能够到:

    发送有关任务的一些进度信息(进度、活动记录等) 如果设置了某些外部信号或属性,则有办法在完成之前中止任务。

我为此实现了一个框架,但这要求所有此类任务都必须引用包含此框架的程序集。

我的问题:.NET 框架(3.5 或更低版本)中是否已经有内置机制来解决上述问题?

我知道我可以使用事件,但这意味着长时间运行的任务必须公开此类事件,我认为这是一种开销。理想情况下,我希望有一个框架可以隐藏多线程问题并且对依赖注入友好,但不依赖于额外的自定义程序集并且不会污染原始接口。

我希望我对问题的描述足够好。如果没有,我可以从我自己的框架中发布一些接口示例。

更新:好的,我认为我的问题描述需要澄清一下 :)。当我说“长时间运行”时,我并不是指工作流意义上的“长时间”。我正在开发一个 WinForms 映射应用程序,它可以做各种各样的事情,比如生成 relief contours。为此,它首先必须从 FTP 服务器下载高程数据文件,解压缩它们,然后执行一些计算。我很早以前就为此编写了代码,但为了使其对 GUI 更友好,我必须重新安装各种检查 - 例如,检测用户已单击 Abort 按钮并停止进程。

所以基本上我关心的是:如何编写可以在以后(如果有的话)在 GUI 环境中使用的代码,您不能简单地在主 GUI 线程中运行所有内容并冻结整个应用程序。 挑战在于找到一种方法,使您的代码适用于 GUI 目的,而无需将其绑定到特定的 GUI 平台。

【问题讨论】:

第一个想法_您可以考虑使用 System.Diagnostics.PerformanceCounter 进行数字活动记录。然后任何知道其名称的应用程序都可以读取数据。要发出中止信号,您可以使用带有命名句柄的 EventWaitHandle。这两种方法都不需要 dll 链接,但它们需要共享的“字符串”命名。或者像@mark 建议的那样走 WWF 路线。 我想避免“干净和无辜”的库代码与等待句柄、线程和 WMI 等棘手的代码混在一起;)我正在寻找这些模式的抽象(最好通过 C#接口,使其更具可测试性)。 【参考方案1】:

这听起来很像Windows Workflow Foundation。

【讨论】:

WWF 对于我所描述的问题来说有点矫枉过正(我已经更新了描述)。【参考方案2】:

看看传奇模式。它没有内置在框架中,但可以实现。或者 NServiceBus 和 MassTransit 都有这个实现。 Arnon RGO 在他的书中有一个草稿(它会完成吗)描述它here。

根据我使用 NServiceBus 的经验,它比 WF 简单得多,而且功能也更强大(尽管我没有看过 WF 4,据所有描述,它几乎是对 WF 的完全重做,因为 Microsoft 已经认识到这个)。

即使您不想要 NServiceBus 或 MassTransit 之类的框架,该模式本身也值得一看,因为它非常适合您所描述的问题空间。

【讨论】:

【参考方案3】:

这取决于您的系统有多复杂。对于相对简单的问题,您可以很好地使用 .NET 2.0 中的 BackgroundWorker class。支持使用OnProgressChanged事件上报操作进度,也支持使用CancelAsync方法取消后台任务。

类是由事件控制的,但由于这已经是类的一部分,我认为这对你来说没有任何开销:

var bw = new BackgroundWorker();
bw.DoWork += new DoWorkEventHandler(DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(RunWorkerCompleted);
bw.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);

执行DoWork方法来运行后台任务(它可以通过调用bw.ReportProgress报告进度并使用bw.CancellationPending检查挂起的取消)。

RunWorkerCompleted 方法在操作完成时在 GUI 线程上执行(这为您提供了一种很好的同步方式而无需担心并发)

每当您的DoWork 方法报告一些进度更改时,都会触发ProgressChanged 事件。

对于更简单的问题,我相信您可以将您的任务表示为后台工作人员。

【讨论】:

@Tomas,我已经将 BackgroundWorker 用于这些目的,但在基础设施级别是间接的。问题在于,将您的任务直接表示为 BackgroundWorker 类意味着它将始终以异步模式运行,但不一定是这种情况。 是的 - 我期待 BackgroundWorker 在你的情况下不会足够强大。希望它可以帮助那些希望在未来更简单的环境中解决此类问题的人。【参考方案4】:

当某事完成或需要更新进度时,我更喜欢使用回调方法向 UI 线程发出信号。您可以传递复杂的对象,并且回调可以返回一个值,以防它需要向工作线程发出信号。并且您可以定义多个回调,具体取决于您需要您的工作人员有多健谈。

【讨论】:

以上是关于.NET:同步长时间运行任务的机制的主要内容,如果未能解决你的问题,请参考以下文章

带有进度通知的长时间运行的后台任务

iOS 在长时间运行任务后显示警报视图

通过将长时间运行的任务分成单独的进程来提高程序性能

在长时间运行的工作中防止更改

保持程序在后台长时间运行-b

分析非常长时间运行的任务