IProgress <t>同步</t>

时间:2013-07-31 23:10:37

标签: c# .net

我在C#中有以下内容

public static void Main()
{
    var result = Foo(new Progress<int>(i =>
        Console.WriteLine("Progress: " + i)));

    Console.WriteLine("Result: " + result);            
    Console.ReadLine();
}

static int Foo(IProgress<int> progress)
{
    for (int i = 0; i < 10; i++)
        progress.Report(i);

    return 1001;
}

Main的一些输出是:

首先运行:

Result: 1001
Progress: 4
Progress: 6
Progress: 7
Progress: 8
Progress: 9
Progress: 3
Progress: 0
Progress: 1
Progress: 5
Progress: 2

第二轮:

Progress: 4
Progress: 5
Progress: 6
Progress: 7
Progress: 8
Progress: 9
Progress: 0
Progress: 1
Progress: 2
Result: 1001
Progress: 3

等...

对于每次运行,输出都不同。如何同步这些方法,以便按照报告的顺序显示进度0,1,... 9,然后是1001的结果。我希望输出如下:

Progress: 0
.
.
.
Progress: 9
Result: 1001

4 个答案:

答案 0 :(得分:12)

进展&lt;&gt; class使用SynchronizationContext.Current属性来Post()进度更新。这样做是为了确保ProgressChanged事件在程序的UI线程上触发,因此更新UI是安全的。必须安全地更新,例如,ProgressBar.Value属性。

控制台模式应用程序的问题是它没有同步提供程序。不像Winforms或WPF应用程序。 Synchronization.Current属性具有默认提供程序,其Post()方法在线程池线程上运行。没有任何联锁,哪个TP线程首先报告其更新是完全不可预测的。没有任何好方法可以联锁。

这里不要使用Progress类,没有意义。您在控制台模式应用程序中没有UI线程安全问题,Console类已经是线程安全的。修正:

static int Foo()
{
    for (int i = 0; i < 10; i++)
        Console.WriteLine("Progress: {0}", i);

    return 1001;
}

答案 1 :(得分:5)

Hans' answer所述,Progress<T>的.NET实现使用SynchronizationContext.Post发送请求。您可以像Yves' answer一样直接进行报告,也可以使用SynchronizationContext.Send,因此请求会一直阻止,直到接收方处理完毕。

因为Reference Source is available实施它就像复制源并将Post更改为Send并将SynchronizationContext.CurrentNoFlow更改为SynchronizationContext.Current一样简单{ {1}}是一个内部财产。

CurrentNoFlow

答案 2 :(得分:3)

正如其他答案之前多次指出的那样,这是由于Progress<T>的实施方式。您可以为您的客户端(库的用户)提供示例代码,或者为控制台项目提供IProgress<T>的实现。这是基本的,但应该这样做。

public class ConsoleProgress<T> : IProgress<T>
{
    private Action<T> _action;

    public ConsoleProgress(Action<T> action) {
        if(action == null) {
            throw new ArgumentNullException(nameof(action));
        }

        _action = action;
    }

    public void Report(T value) {
        _action(value);
    }
}

答案 3 :(得分:1)

这是Progress<T>编写方式的线程问题。您需要编写自己的IProgress<T>实现来获得所需。

但是,这个场景已经告诉你一些重要的事情,尽管在这个例子中,你只是做简单的Console.Writeline语句,在实际场景中,由于需要更长或更短的时间,某些报告可能会以其他顺序报告在我看来,你不应该依赖订单。