表单级别的进度声明给出了例外

时间:2014-09-28 22:58:53

标签: c# async-await

在WPF窗口中,在控制台应用程序中启动,我尝试了这个例子,以便在async-await中使用IProgress:

namespace Test
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
public partial class Main : Window
{
    // class level declaration
    Progress<string> Progress1;

    public Main()
    {
        InitializeComponent();

       // initialization of class level field
        Progress1 = new Progress<string>(value =>
        {
            Print(value);
        });
    }

    private async void Button_Click(object sender, RoutedEventArgs e)
    {
        Print("Start test");

        // local variable 
        Progress<string> Progress2 = new Progress<string>(value =>
        {
            Print(value);
        });

        // interface (normally Progress1/2 would be passed as a parameter)
        // Progress2 works fine, Progress1 gives an exception
        var progress = Progress1 as IProgress<string>;

        await Task.Run(() =>
        {
            for (int i = 0; i != 5; ++i)
            {
                if (progress != null) progress.Report("\nStage " + i);
                Thread.Sleep(1000);
            }
        });

        Print("\nCompleted.");
    }

    void Print(string text)
    {
        //Output is a WPF TextBlock
        Output.Inlines.Add(new Run(text) { Foreground = Brushes.Blue, FontWeight = FontWeights.Normal });
    }
}

局部变量Progress2工作正常:回调位于UI线程中 类级别字段Progress1给出了一个例外。这个回调显然在后台线程中。

  

调用线程无法访问此对象,因为它不同   线程拥有它。

这可能与匿名函数和回调的工作方式有关 有人能解释一下这个问题吗?

EDIT
在正常的WPF解决方案中,问题无法重现 它可以在控制台应用程序中重现,其中WPF窗口的启动方式如下:

Application app = new Application ();
app.Run(new Main());

在这种情况下Synchronization.Current == null在构造函数中,!= null在{ Loaded事件。正如答案和评论中所解释的那样。

1 个答案:

答案 0 :(得分:4)

是的,购买Microsoft雪茄用于在Winforms中构建此诊断程序,因为否则会出现完全不可解决的问题,只会偶尔崩溃您的代码。

这是初始化顺序问题,它是由您的Progress变量初始化太快引起的。请记住Progress&lt;&gt; class,它知道如何运行你在“正确的线程”上传递的委托的目标。特别是,您希望它在您的UI线程上运行,以便您可以安全地更新标签。进展&LT;&GT;通过制作SynchronizationContext.Current的副本,稍后使用它来使用Post()方法运行委托目标。

问题是,Current属性还没有值。这种情况发生在几微秒之后,当Form基类构造函数运行时。进展&LT;&GT;复制了null,它现在唯一能做的就是在线程池线程上运行Post()方法目标。 KABOOM!

修复很简单:

Progress<string> Progress;

public Form1() {
    InitializeComponent();
    Progress  = new Progress<string>(value => {
        label.Text = value;
    });
}

现在它稍后被初始化,Synchronization.Current有一个值。