使用IProgress.Report()报告进度时,控制台消息以不正确的顺序显示 [英] Console messages appearing in incorrect order when reporting progress with IProgress.Report()

查看:107
本文介绍了使用IProgress.Report()报告进度时,控制台消息以不正确的顺序显示的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我注意到以下行为。由IProgress填充控制台输出消息时,它们会出现在错误的文件夹中。

I have noticed a following behaviour. Console output messages appear in an incorrect folder when they are populated by IProgress.

var recounter = new IdRecounter(filePath, new Progress<string>(Console.WriteLine));
                recounter.RecalculateIds();

我正在尝试提高封装性,可重用性和设计技能。
因此,我有一个名为IdRecounter的类。我现在想在控制台应用程序中使用它,但是稍后也许在WPF应用程序中使用它。

I am trying to improve my encapsulation, reusability and design skills. So, I have a class called IdRecounter. I want to use it in a console app for now, but later on perhaps in a WPF app or whatever.

因此,我希望该类完全不知道它是环境-但同时我想报告实时操作的进度-因此,我使用的是IProgress类型,该类型可让我将内容放入控制台或日志文件中,或更新状态标签属性等(请告诉我您是否不是这样做的方法)。

Therefore, I want the class to be completely unaware of it's environment - but at the same time I want to report progress of action 'live' - therefore, I am using the IProgress type, which will allow me to put stuff into console, or into a log file, or update a status label property etc. (Please let me know if that's not how you do it)

因此,我注意到它倾向于以错误的顺序将消息放入控制台。
处理文件1

处理文件4

处理文件5

处理文件3

全部完成!

处理文件2

So, I have noticed that it tends to throw messages into console in an incorrect order, e.g. Processing File 1
Processing File 4
Processing File 5
Processing File 3
All done!
Processing File 2

当我使用以下命令切换IProgress( MyProgress.Report())时 Console.WriteLine()可以按预期工作。

这是什么原因以及如何对其进行控制?

When I switched IProgress (MyProgress.Report()) with Console.WriteLine() it works as expected.
What is the reason of that and how can it be controlled?

谢谢

推荐答案

Progress< T> 类使用当前的同步上下文为创建它的线程调用其 ProgressChanged 事件的事件处理程序。

The Progress<T> class uses the current synchronization context for the thread in which it was created to invoke the event handlers for its ProgressChanged event.

在控制台应用程序中,默认的同步上下文使用线程池来调用委托,而不是将它们编组回检索上下文的线程中。这意味着每次更新进度时,事件处理程序都可能在不同的线程中调用(尤其是进度更新快速连续发生时。)。

In a console application, the default synchronization context uses the thread pool to invoke delegates, rather than marshaling them back to the thread where the context is retrieved. This means that each time you update progress, the event handler may be invoked in a different thread (especially if the progress updates occur in quick succession).

线程是按计划进行的,因此不能保证在另一个线程池工作器实际上将运行之前,另一个线程池工作器在另一个线程运行任务之前先为其分配任务。尤其是对于相对简单的任务(例如发出进度消息),很容易发生这样的情况,即稍后排队的任务实际上是在较早排队的任务之前完成的。

Due to the way threads are scheduled, there is no guarantee that a thread pool worker assigned a task before another thread pool worker will actually run its task before that other worker runs its task. Especially for relatively simple tasks (such as emitting progress messages), it can easily be the case then that tasks enqueued later are actually completed before tasks enqueued earlier.

为了确保进度更新消息能够按顺序显示,您需要使用其他机制。例如,您可以使用 BlockingCollection< T> 设置生产者/消费者,在这里,您有一个单线程消费消息,这些消息由报告进度的操作排队(产生) 。或者,当然,您可以直接调用 Console.WriteLine()(因为您已经验证过可以使用)。

If you want for your progress update messages to be guaranteed to be displayed in order, you'll need to use a different mechanism. For example, you could set up a producer/consumer with BlockingCollection<T>, where you have a single thread consuming messages that are queued (produced) by your operations that report progress. Or, of course, you could just call Console.WriteLine() directly (as you have already verified will work).

请注意,这并不意味着您需要放弃使用 IProgress< T> 的想法。这仅意味着至少在控制台情况下,您需要提供自己的实现,而不是使用 Progress< T> 类。例如:

Note that that doesn't mean you need to abandon the idea of using IProgress<T>. It just means you would need to provide your own implementation, rather than using the Progress<T> class, at least in the console scenario. For example:

class ConsoleProgress : IProgress<string>
{
    public void ReportProgress(string text)
    {
        Console.WriteLine(text);
    }
}

例如,这将允许您保留 IdRecounter()类中的 IProgress< T> 抽象,将该类型与UI上下文分离。可以将其重新用于控制台程序以及任何GUI API程序,例如Winforms,WPF,Winrt等。

This would allow you to, for example, keep the IProgress<T> abstraction in the IdRecounter() class, decoupling that type from the UI context. It could be reused for a console program as well as any GUI API program, such as Winforms, WPF, Winrt, etc.



底线:进度< T> IProgress< T> 的非常有用的实现,当您需要抽象十字架时-线程,GUI程序中需要与同步上下文相关的操作。它可以在控制台程序中工作,但是因为在这种情况下它将使用线程池,所以您可能无法获得确定性排序的输出,至少在不包括与 ProgressChanged 的附加同步的情况下事件处理程序。


The bottom line: Progress<T> is a very useful implementation of IProgress<T> when you need to abstract the cross-thread, synchronization context-related operations that are needed in a GUI program. It will work in console programs, but because it will use the thread pool in that case, you may not get deterministically ordered output, at least not without including additional synchronization to the ProgressChanged event handlers.

这篇关于使用IProgress.Report()报告进度时,控制台消息以不正确的顺序显示的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆