MvvmCross - 当我调用Task.Run时,UI冻结

时间:2014-01-17 09:54:39

标签: c# wpf mvvmcross

任何人都可以告诉我为什么在重度计算期间UI会冻结:

var ret = await Task.Run(() => this._service.CalcFourier(100000, progress));

我有一个绑定'Value'属性的进度条,但在计算完成之前不会更新。当我将断点设置为'CurrentCalcCount'的setter方法并开始计算时,每次都调用'RaisePropertyChanged'。

如何避免阻止UI ...我整天都在努力,我不知道为什么。任何机构都可以修复代码或者有一些解释来帮助我吗?感谢。

CalcView.xaml

    <Grid Grid.Row="0">
        <TextBlock Text="Calculate pi with fourier transform method." Margin="3" />
    </Grid>
    <StackPanel Grid.Row="1" Orientation="Horizontal" HorizontalAlignment="Center">
        <TextBlock Text="Calc time:" Margin="3" VerticalAlignment="Center"/>
        <TextBox Text="{Binding CalculationCount, UpdateSourceTrigger=PropertyChanged, Mode=TwoWay}" MinWidth="150" Margin="3"/>
        <Button Content="Start" Margin="3,0,3,3" Command="{Binding CalcCommand}" />
    </StackPanel>
    <StackPanel Grid.Row="2">
        <ProgressBar MinHeight="25" Margin="5" Maximum="{Binding CalculationCount}" Value="{Binding CurrentCalcCount}" />
        <Button Content="Cancel" MaxWidth="100" Command="{Binding CancelCommand}"/>
    </StackPanel>
    <TextBlock Grid.Row="3" Text="{Binding ProgressText}" HorizontalAlignment="Center"/>

'CalcCommand'和其他属性定义如下:

CalcViewModel.cs

    private MvxCommand _calcCommand;

    public ICommand CalcCommand
    {
        get
        {
            return _calcCommand ??
                   (_calcCommand = new MvxCommand(DoCalcCommand, () => !InProgress));
        }
    }

    private async void DoCalcCommand()
    {
        try
        {
            InProgress = true;

            var progress = new Progress<CalcProgressInfo>(info => Dispatcher.RequestMainThreadAction(() =>
            {
                CurrentCalcCount = info.CurrentCount;
                ProgressText = info.Message;
            }));

            var ret = await Task.Run(() => this._service.CalcFourier(this._calculationCount, progress));

            ProgressText += Environment.NewLine + ret;
        }
        catch (Exception e)
        {
            ProgressText = e.Message;
        }
        finally
        {
            InProgress = false;
        }
    }
模型中的

CalcFourier命令:

CalcService.cs

    private bool _cancel;

    public double CalcFourier(int count, IProgress<CalcProgressInfo> progress)
    {
        var sigma = 0.0;
        var n = 1;

        foreach (
            var i in
                Enumerable.Range(1, count).TakeWhile(_ => !_cancel))
        {
            progress.Report(new CalcProgressInfo(i,
                string.Format("Calculation is in progress now. - {0}/{1}", i, count)));

            sigma += 1.0 / n / n;
            n += 2;
        }

        progress.Report(this._cancel
            ? new CalcProgressInfo(0, "Calculation has canceled.")
            : new CalcProgressInfo(count, "Calculation has completed."));
        _cancel = false;

        return Math.Sqrt(sigma * 8);
    }

1 个答案:

答案 0 :(得分:0)

看起来您的处理代码充满了更新的UI。完成一对双精度分割和整数加法不需要很长时间;只需构建进度报告字符串几乎肯定会花费更长的时间。

而不是:

progress.Report(new CalcProgressInfo(i,
    string.Format("Calculation is in progress now. - {0}/{1}", i, count)));

考虑尝试这个:

if (i % 100 == 0)
{
    progress.Report(new CalcProgressInfo(i,
        string.Format("Calculation is in progress now. - {0}/{1}", i, count)));
}

使用更高/更低的值来替换100,直到您能以合理的速度(可读但不会太慢)看到您的更新。

您根本没有看到任何更新的原因是由于UI消息循环的优先级排序;诸如“更新此进度值”的“代码”消息具有比“绘制”消息更高的优先级。因此,如果循环充满了更新消息,它就永远无法重新绘制UI。

P.S。没有必要使用Dispatcher;默认情况下,Progress<T>将封送到相应的主题:

var progress = new Progress<CalcProgressInfo>(info =>
{
    CurrentCalcCount = info.CurrentCount;
    ProgressText = info.Message;
});