3

I am updating my UI via IProgress and the Report method. I do something like

IProgress<ProgressInfo> p = new Progress<ProgressInfo>(ReportProgress);
Task task = Task.Factory.StartNew(() =>
{
    result = Execute(p);
});

where in the Execute method I report progress via

p.Report(someObject);

Now, the issue is, in the last case where I report progress to the UI, I write something to a console window, which I use when the task has returned. The problem is that because IProgress is async, the task returns when the last progress report has not finished, leaving me without data.

I clearly could put the thread to sleep or spinwait, but this seems nasty to me as, although the report process will take a very short amount of time, I don't know the actual value to sleep etc. I have looked at using AutoResetEvent but again I am not sure this is the cleanest solution.

My question is how can I force the thread to wait for the return of the p.Report(someObject) call in the "cleanest" way possible, that is without relying on guessing at the time the report progress will take to return?

MoonKnight
  • 23,214
  • 40
  • 145
  • 277
  • You should not be using `IProgress` to fetch data from the UI. If you're fetching data then clearly you're not reporting progress, and so you're using the wrong tool for the job. – Servy Jan 09 '15 at 15:16
  • I am not using `IProgress` to _fetch_ data from the UI. Where does it say that? – MoonKnight Jan 09 '15 at 15:21
  • `the task returns when the last progress report has not finished, leaving me without data` Clearly your "progress reporting" mechanism is returning data. It shouldn't be reporting any data. That's why `IProgress` has asynchronous fire-and-forget progress reporting in the first place. If it needs to be asking the UI for information you should be using a different mechanism. – Servy Jan 09 '15 at 15:24
  • No, you miss understand. I am writing text to a UI editor control for progress reporting at run time. When the process is finished and the `Task` completes, I write the contents of the UI editor text control to file. The issue is that once the processing is done and the `Task` completes, the text that is written to file is missing the last part of the editor text (that was written to my console window using `IProgress`). I could store the text as a `string` as well as using the editor text and pass that back using `Task` but I want to know how I can merely wait for my report to finish. – MoonKnight Jan 09 '15 at 15:40
  • The point stands, you should not be using progress reporting to fetch data, which clearly you're doing. You should also not be storing your data solely in your UI; your UI should be for presenting the data that you're storing explicitly in memory. If you design your program better you won't have this problem. – Servy Jan 09 '15 at 15:44
  • 1
    I am not _fetching_ anything. I am writing text to a control, which is _not_ against what `IProgress` should be used for at all. Once this task is complete I have all the data I need held in that control. I don;t see a major issue with this. As I said, I could store the text in a string _as well_, but as for designing the program better, perhaps I could, however, there is no way for me to hook in to the editor to see when text has been added, as it is third party. So the option I am left with is to duplicate the string data in a `string` and send is back to the UI thread via `Task`... – MoonKnight Jan 09 '15 at 16:06
  • But you *are* fetching something. Writing out information to a control is indeed an appropriate use of `IProgress`, but the fact that you want to get information back as a result is very much *not* in line with its design. That's made crystal clear by the fact that reporting progress is fire-and-forget in `IProgress`. It's a mechanism for the UI to be notified of non-UI work's progress, not for your non-UI work to be notified of the UI's progress. You say you don't see a major issue with storing all of your data in UI controls, and yet you're having this exact problem as a result. – Servy Jan 09 '15 at 16:09
  • I am not sure what I have not made clear here. I am updating the UI from non-UI using `IProgress`, you will agree this is fine. I am writing progress output to a window, again fine. 1. UI yields control to non-UI. 2. non-UI does stuff and reports to UI. 3. Non-UI completes. 4. UI gets info from a control and writes to file. Nothing disastrously wrong with the the use of `IProgress`. It is the writing to file immediately after this thread had returned and using the control text which is probably not the _done thing_. I know what I must do, thanks for your time... – MoonKnight Jan 09 '15 at 16:17
  • You've made it quite clear exactly what you're doing. I'm telling you that you shouldn't be doing all of those things, and explained why. – Servy Jan 09 '15 at 16:19
  • And I am saying that it is only step four where I agree with you (that it is not the "right" thing to do). Not that you need my agreement, but you are not correct in saying that my use of `IProgress` in wrong in this case (steps 1 - 3). Have a good weekend and thanks for your time, it is appreciated. – MoonKnight Jan 09 '15 at 16:24
  • IMHO This should have been included in the framework. +1. – ispiro Nov 01 '17 at 23:06

1 Answers1

4

The Progress<T> class uses a SynchronizationContext to invoke the reporting handler in an asynchronous manner. This is done to update the UI on the UI thread in WPF or WinForms. If that's not what you want (and it obviously isn't needed in your Console application), don't use the IProgress<T> interface at all, or implement it yourself in a synchronous way:

  1. Go to ReferenceSource and duplicate the code into your project.

  2. Replace

    m_synchronizationContext.Post(m_invokeHandlers, value);
    

    with

    m_synchronizationContext.Send(m_invokeHandlers, value);
    
Frank
  • 4,461
  • 13
  • 28
  • It is not a "Console" application. It is a WinForms application that _has_ a console/output window. I need to update async. – MoonKnight Jan 09 '15 at 14:53