I have the following in C#
public static void Main()
{
var result = Foo(new Progress(i =>
Console.WriteLine(\"Progress: \" + i)));
As said in Hans' answer, the .NET implementation of Progress uses SynchronizationContext.Post to send its requests. You could make it directly report like in Yves' answer or you could use SynchronizationContext.Send so the request will block till the receiver has processed it.
Because the Reference Source is available implementing it is as easy as copying the source and changing the Post to a Send and changing SynchronizationContext.CurrentNoFlow to SynchronizationContext.Current due to CurrentNoFlow being an internal property.
///
/// Provides an IProgress{T} that invokes callbacks for each reported progress value.
///
/// Specifies the type of the progress report value.
///
/// Any handler provided to the constructor or event handlers registered with
/// the event are invoked through a
/// instance captured
/// when the instance is constructed. If there is no current SynchronizationContext
/// at the time of construction, the callbacks will be invoked on the ThreadPool.
///
public class SynchronousProgress : IProgress
{
/// The synchronization context captured upon construction. This will never be null.
private readonly SynchronizationContext m_synchronizationContext;
/// The handler specified to the constructor. This may be null.
private readonly Action m_handler;
/// A cached delegate used to post invocation to the synchronization context.
private readonly SendOrPostCallback m_invokeHandlers;
/// Initializes the .
public SynchronousProgress()
{
// Capture the current synchronization context. "current" is determined by Current.
// If there is no current context, we use a default instance targeting the ThreadPool.
m_synchronizationContext = SynchronizationContext.Current ?? ProgressStatics.DefaultContext;
Contract.Assert(m_synchronizationContext != null);
m_invokeHandlers = new SendOrPostCallback(InvokeHandlers);
}
/// Initializes the with the specified callback.
///
/// A handler to invoke for each reported progress value. This handler will be invoked
/// in addition to any delegates registered with the event.
/// Depending on the instance captured by
/// the at construction, it's possible that this handler instance
/// could be invoked concurrently with itself.
///
/// The is null (Nothing in Visual Basic).
public SynchronousProgress(Action handler) : this()
{
if (handler == null) throw new ArgumentNullException("handler");
m_handler = handler;
}
/// Raised for each reported progress value.
///
/// Handlers registered with this event will be invoked on the
/// captured when the instance was constructed.
///
public event EventHandler ProgressChanged;
/// Reports a progress change.
/// The value of the updated progress.
protected virtual void OnReport(T value)
{
// If there's no handler, don't bother going through the [....] context.
// Inside the callback, we'll need to check again, in case
// an event handler is removed between now and then.
Action handler = m_handler;
EventHandler changedEvent = ProgressChanged;
if (handler != null || changedEvent != null)
{
// Post the processing to the [....] context.
// (If T is a value type, it will get boxed here.)
m_synchronizationContext.Send(m_invokeHandlers, value);
}
}
/// Reports a progress change.
/// The value of the updated progress.
void IProgress.Report(T value) { OnReport(value); }
/// Invokes the action and event callbacks.
/// The progress value.
private void InvokeHandlers(object state)
{
T value = (T)state;
Action handler = m_handler;
EventHandler changedEvent = ProgressChanged;
if (handler != null) handler(value);
if (changedEvent != null) changedEvent(this, value);
}
}
/// Holds static values for .
/// This avoids one static instance per type T.
internal static class ProgressStatics
{
/// A default synchronization context that targets the ThreadPool.
internal static readonly SynchronizationContext DefaultContext = new SynchronizationContext();
}