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

7
问题描述: 您正在编写一个包含某些算法/任务的库,这些算法/任务可能需要很长时间才能完成,原因有各种:计算、文件系统、网络通信等。您希望能够:
  1. 发送关于任务的一些进度信息(进度、活动日志等)
  2. 在完成之前有一种方式可以中止任务,如果设置了某些外部信号或属性。

我实现了一个框架来解决这个问题,但这要求所有这样的任务都必须引用包含此框架的程序集。

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

我知道我可以使用事件,但这意味着长时间运行的任务将不得不公开这些事件,我认为这是一种负担。理想情况下,我希望有一个框架,可以隐藏多线程问题,并支持依赖注入 ,但不会依赖于其他自定义程序集,并且不会污染原始接口。

我希望我已经足够清楚地描述了这个问题。如果没有,我可以发布一些来自我自己框架的接口示例。

更新:好的,我认为我的问题描述需要有一点澄清 :)。当我说“长时间运行”时,我不是指“工作流程”的长度。我正在开发一个WinForms映射应用程序,它可以执行各种操作,例如生成等高线。要做到这一点,首先必须从FTP服务器下载高程数据文件,解压缩它们,然后执行一些计算。我很久以前就写了这个代码,但为了使其对GUI更加友好,我必须进行各种检查——例如,检测用户是否单击了中止按钮并停止进程。

因此,我的关注点基本上是:如何编写适用于GUI环境的代码,您不能仅在主GUI线程中运行所有内容并冻结整个应用程序。挑战是找到一种方法,使您的代码适用于GUI目的,而不将其绑定到特定的GUI平台。


首要想法_ 您可以研究使用System.Diagnostics.PerformanceCounter进行数字活动日志记录。然后,任何知道其名称的应用程序都可以读取数据。要发出中止信号,可以使用具有命名句柄的EventWaitHandle。这两种方法都不需要dll链接,但它们需要共享的“字符串”命名。或者像@mark建议的那样走WWF路线。 - Mikael Svenson
我想避免让“干净无辜”的库代码与像等待句柄、线程和WMI这样的复杂代码混在一起。我正在寻找这些模式的抽象(最好通过C#接口实现,以使其更易于测试)。 - Igor Brejc
4个回答

6

WWF对于我所描述的问题有些过度(我已经更新了描述)。 - Igor Brejc

2
看一下 saga 模式。它并没有内置在框架中,但可以实现。另外,NServiceBus 和 MassTransit 都有其实现。Arnon RGO 在他的书稿中描述了它,这里

根据我的经验,与 WF 相比,使用 NServiceBus 起步更简单,而且功能更强大(尽管我还没有看过 WF 4,根据所有描述,WF 4 是对 WF 的近乎完全重构,因为 Microsoft 已经认识到了其失败之处)。

即使您不想使用像 NServiceBus 或 MassTransit 这样的框架,该模式本身也非常值得研究,因为它与您所描述的问题空间非常吻合。

1

这取决于你的系统有多复杂。对于相对简单的问题,你可以很好地使用.NET 2.0中的BackgroundWorker。它支持使用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检查待处理的取消请求)。

  • 当操作完成时,在GUI线程上执行RunWorkerCompleted方法(这为您提供了一种不必担心并发问题的同步方式)

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

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


@Tomas,我已经在基础设施层间接地使用BackgroundWorker来实现这些目的。问题是,将你的任务直接表示为BackgroundWorker类意味着它将始终以异步模式运行,这并不一定是正确的。 - Igor Brejc
是的 - 我预料到在你的情况下BackgroundWorker不够强大。希望它能帮助未来有人在更简单的环境中解决这种问题。 - Tomas Petricek

0

我更喜欢使用回调方法来通知UI线程何时完成某些操作或需要更新进度。您可以传递复杂对象,如果需要向工作线程发出信号,则回调可以返回值。并且,您可以定义多个回调,具体取决于您需要工作线程有多少交互。


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