来自Progress<T> Class 参考页
提供给构造函数或在ProgressChanged事件中注册的事件处理程序的任何处理程序都通过构造实例时捕获的SynchronizationContext实例调用。如果在构建时没有当前的SynchronizationContext,则将在ThreadPool上调用回调。
我正在后台线程上创建一个Progress<int>。我希望回调和任何相关的取消(抛出一个OperationCancelledException)都发生在同一个线程上。
在构造Progress<int>对象时,SynchronizationContext.Current是null。
因此,正如上面的文档所告诉我的,回调是在线程池上执行的。
问题
SynchronizationContext是null这一事实,可以做些什么吗?例如,为当前线程创建一个?Progress<T>会捕获那个SynchronizationContext吗?...a SynchronizationContext不一定表示特定的线程;它还可以将传递给它的委托转发到多个线程中的任何一个(例如,一个ThreadPool工作线程).
解决办法/解决办法
为了确保回调发生在同一个线程上,我使用了自己的IProgress<T>接口实现:
public class SynchronousProgress<T> : IProgress<T>
{
private readonly Action<T> action;
public SynchronousProgress(Action<T> action)
{
this.action = action;
}
public void Report(T value)
{
action(value);
}
}它起作用了。但是,我仍然想知道是否有一种方法可以通过.NET Progress<T>类来实现这一点?
最新情况:背景资料
Progress<T>类的尝试使用位于一个自定义的可取消进度对话框中,该对话框封装了一些工作和报告进度。在这种情况下,工作(可以取消)发生在插件边界的另一边。希望在插件接口中使用.NET类型(例如IProgress<T>)来通信进度,而不是使用自定义类型(例如我们自己的,(旧的) IProgress类型)。
给.NET IProgress<T>实现的回调只是一条增加自定义IProgress实现进度的指令。大致如下:
public void Export(CulturedStreamWriter writer, IProgress progress) // that's a custom IProgress
{
progress.Steps = toExport.Count;
exporter.Export(toExport, writer, new SynchronousProgress<int>(progress.StepTo)); // increment the progress of the custom IProgress
}使用.NET Progress<T>代替SynchronousProgress<T>不起作用,因为取消异常将抛出到与此代码不同的线程上,这是需要捕获它们的地方。
似乎.NET IProgress<T>的自定义实现正在工作(SynchronousProgress<T>),也许它实际上是最合适的方法(考虑到周围的代码/约束)。
发布于 2016-12-20 12:35:27
可以在构造实例之前将SynchronizationContext.Current设置为您选择的值。事后重置它(使用一个finally块,以确保不会永久地破坏线程)。
这有点丑。WebClient需要同样的东西(与这个问题无关--只是一个例子)。我发现Progress<T>中的API遗漏了不能提供同步上下文。您可以考虑将GitHub上的一个问题作为一项公共服务。
如果需要,只需将Progress<T>的源代码分叉,并为同步上下文添加构造函数参数。这是一门小型的、独立的课程。
如果有可能,这是否意味着回调在同一个线程上执行?
它们将在同步上下文选择运行它们的任何地方运行。取决于背景。
您自己的实现现在只运行回调,这似乎毫无意义。这个IProgress实现的行为就像一个不了解线程的事件。它不会特别针对任何线程。我怀疑这是你需要的,虽然我不能肯定。
https://stackoverflow.com/questions/41239945
复制相似问题