C# 如何从长期运行的操作中获得中间结果?
假设C# 如何从长期运行的操作中获得中间结果?,c#,events,dependency-injection,system.reactive,C#,Events,Dependency Injection,System.reactive,假设Calculate是一个计算量非常大的函数 class Algorithm { FinalResultObject Calculate() { longPartialCalculation(); //signal to caller that that part is ready of type MidResult1 morePartialCalculation(); //signal more is ready
Calculate
是一个计算量非常大的函数
class Algorithm
{
FinalResultObject Calculate()
{
longPartialCalculation();
//signal to caller that that part is ready of type MidResult1
morePartialCalculation();
//signal more is ready, different type of MidResult2
moreWork();
return finalResult;
}
}
现在假设,中间结果需要随时向用户显示
我看到的选择是:
有什么提示吗?解决这个问题的方法是定义一个冷的可观察的,如下所示:
IObservable<Result> Calculate(IScheduler scheduler)
{
return Observable.Create<Result>(observer =>
scheduler.Schedule(() =>
{
observer.OnNext(longPartialCalculation());
observer.OnNext(morePartialCalculation());
observer.OnNext(moreWork());
observer.OnCompleted();
}));
}
// Depending upon your needs, you could use inheritance as follows:
public abstract class Result { ... }
public class MidResult1 : Result { ... }
public class MidResult2 : Result { ... }
public class FinalResultObject : Result { ... }
请注意,Rx的主要优点之一是能够查询;e、 例如,一个简单的过滤器:
algo.Calculate(ThreadPoolScheduler.Instance)
.Where(result => result.HasRequiredState)
.ObserveOnDispatcher()
.Subscribe(result => handle(result.RequiredState));
您可以使用.Net的
Progress
。创建实例,将处理程序传递或注册到其事件,并在整个长时间运行的过程中通过它进行报告:
var progress = new Progress<string>(value => Console.WriteLine(value));
Calculate(progress);
FinalResultObject Calculate(IProgress<string> progress)
{
longPartialCalculation();
progress.Report("MidResult1");
morePartialCalculation();
progress.Report("MidResult2");
moreWork();
return finalResult;
}
var progress=newprogress(value=>Console.WriteLine(value));
计算(进度);
FinalResultObject计算(IProgress进程)
{
长部分计算();
进度报告(“中期结果1”);
更多部分计算();
进度报告(“中期结果2”);
moreWork();
返回最终结果;
}
在本例中,报告正在向控制台写入字符串,但您当然可以将其用于所需的任何类型
Progress
还可以在创建时捕获当前的SynchronizationContext
,这样您就可以在UI线程中创建它,并将其传递给非UI线程,而不会出现任何同步问题。除非此工作人员将比其报告进度的任何工作人员活得更长(这几乎永远不会发生)没有必要取消订阅活动;持有事件的对象应该在任何对象向其添加处理程序之前死亡。我不确定是否理解此问题,但除了提供特殊的进度报告事件外,您还可以轮询正在进行的操作,这样您就不需要事件,订阅和取消订阅。我对这个问题的理解不是如何从一些封闭的操作中获得进度,而是如何报告计算所产生的进度。这只是一个如何公开进度通知的问题。@Servy那么你是说应该通过事件来实现吗?UI肯定会比算法
类更有效。但是由于Calculate()
不会在UI线程上运行,所以我需要将事件处理程序传回调度程序。@各位:实际上我在谷歌搜索这个问题时遇到了麻烦。似乎我真的找不到我的问题的真实世界样本,我想知道,因为它感觉很普遍。我需要封装算法
类,因为它需要可互换。中间步骤没有特定的顺序,这就是为什么我不能公开一些方法,比如CalculationStep1()
等等。我考虑过只使用一个可观察的方法。我在解释抽象的结果
类(甚至是接口)时遇到了困难,因为它可能共享的只是公共对象值{get;}
,这意味着在OnNextResult()中进行类型检查和强制转换。另一方面,它可能为空,在继承类上留下显式类型化的属性。然后,结果
类的唯一目的是将所有结果合并到一个可观察的结果中。这就是为什么我想在算法
类上公开几个可观察对象(每个结果一个)。拥有多个可观察对象的问题是同步。Observable表示Rx中的并发性,因此有多个Observable意味着,例如,两个Observable可以同时接收通知。当然,无论如何,您都在封送到UI线程,尽管在分派器的可观察对象之间仍然可能存在竞争,这取决于您将如何对其进行编码。无论如何,在Rx中,在一个可观察对象中建模一系列通知更为自然——这就是它的用途!Result
类只是一种承载附加信息的方式。也许你根本不需要它。只需使用IObservable
,其中T
是当前计算值;e、 例如,IObservable
可以表示完成百分比,例如在调用OnCompleted
之前立即推送0,中途推送50,然后立即推送100。继承的想法只是一个建议。如果您需要成对的数据,您还有其他选择;e、 例如,您可以在枚举中对该类进行编码,并将其作为结果
类的属性提供,或者您可以使用元组
。Result
类只是比Tuple
更显式的一种方式,因为使用语义属性总是比使用Item1
、Item2
等更好。你可能会发现我的一篇博文很有帮助:有趣!这是我以前从未遇到过的一门课!它看起来非常类似于RX的观测结果,但同样的问题也出现了:由于中间结果的类型不同,我需要进行类型检查和类型转换或进度
。谢谢你的选择@这取决于你打算通知什么。最简单的选项是百分比,因此进度
。我可能会做的是创建一个包含所有阶段的CalculateProgress
enum,并在每个阶段完成时报告。。。。然后在enum?@MarkusHütter上拉取每个阶段的结果。同样,这取决于您使用的是什么
var progress = new Progress<string>(value => Console.WriteLine(value));
Calculate(progress);
FinalResultObject Calculate(IProgress<string> progress)
{
longPartialCalculation();
progress.Report("MidResult1");
morePartialCalculation();
progress.Report("MidResult2");
moreWork();
return finalResult;
}