来自Progress<T> Class
reference page:
提供给使用ProgressChanged事件注册的构造函数或事件处理程序的任何处理程序都是通过构造实例时捕获的SynchronizationContext实例调用的。如果在构造时没有当前的SynchronizationContext,则将在ThreadPool上调用回调。
我在后台线程上创建Progress<int>
。我希望回调以及任何相关的取消(抛出OperationCancelledException
)在同一个线程上发生。
在构建Progress<int>
对象时,SynchronizationContext.Current
为null
。
因此,正如上面的文档告诉我的那样,回调正在线程池上执行......
SynchronizationContext
为null
的事实采取任何措施?例如,为当前线程创建一个?Progress<T>
是否会 SynchronizationContext
... 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>
类位于自定义的可取消进度对话框中,该对话框封装了一些工作和报告进度。在这种情况下, work (可以取消)出现在插件边界的另一侧。在插件界面中使用.NET类型(例如IProgress<T>
)来传达进度,而不是自定义类型(例如我们自己的,(较旧的)IProgress
类型),这是可取的。 / p>
给予.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>
),或许它实际上是最合适的方法(考虑到周围的代码/约束)。
答案 0 :(得分:1)
您可以在将实例构造为您选择的值之前设置SynchronizationContext.Current
。之后重置它(使用finally
块以确保不会永久地弄乱线程。)
这有点难看。 WebClient
需要相同的东西(与此问题无关 - 只是一个例子)。我发现Progress<T>
中的API遗漏是您无法提供同步上下文的。您可以考虑在GitHub上打开一个公共服务问题。
如果您希望只能分叉Progress<T>
的源代码并为同步上下文添加构造函数参数。这是一个小型,独立的课程。
如果可能,那是否可靠意味着回调是在同一个线程上执行的?
它们将在同步上下文选择运行它们的任何地方运行。取决于具体情况。
你自己的实现现在只是运行回调,这看起来毫无意义。此IProgress
实现的行为就像一个对线程一无所知的事件。它不会特别针对任何线程。我怀疑这是你需要的,虽然我不能确定。