Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
231 views
in Technique[技术] by (71.8m points)

c# - Console messages appearing in incorrect order when reporting progress with IProgress.Report()

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();

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.

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)

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

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?

Thanks

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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.

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).

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);
    }
}

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.


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.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...