【问题标题】:UI thread is blockedUI线程被阻塞
【发布时间】:2016-01-11 17:05:51
【问题描述】:

在 MainWindow 中,我有 async 显示忙碌指示器的方法

public async Task BusyLoaderAsync(Action doWorkAction)
    {
        using (var tokenSource = new CancellationTokenSource())
        {
            await loadingPanel.StartSpinAsync(tokenSource.Token).ConfigureAwait(false);
            await this.Dispatcher.InvokeAsync(doWorkAction);

            tokenSource.Cancel();

            await loadingPanel.StopSpinAsync().ConfigureAwait(false);
        }
    }

加载面板看起来像:

<Grid Panel.ZIndex="1000" HorizontalAlignment="Stretch" Grid.RowSpan="3" Visibility="{Binding PanelLoading, Converter={StaticResource BoolToVis}}">
        <controls:LoadingPanel x:Name="loadingPanel" VerticalAlignment="Stretch"
                    IsLoading="{Binding PanelLoading}"
                    Message="Calculating..."
                    SubMessage="Wait a minute"
                               />
    </Grid>

控制加载面板有 2 个额外的方法:

public async Task StartSpinAsync(CancellationToken cancellationToken)
    {
        int delay;
        if (!int.TryParse(ConfigurationManager.AppSettings["ApplicationDelay"], out delay))
        {
            delay = 0;
        }
        await Task.Delay(delay, cancellationToken);
        await this.Dispatcher.InvokeAsync(() => IsLoading = true,
            System.Windows.Threading.DispatcherPriority.Normal, cancellationToken);
    }

    public async Task StopSpinAsync()
    {
        await this.Dispatcher.InvokeAsync(() => IsLoading = false,
            System.Windows.Threading.DispatcherPriority.Normal);
    }

我们希望仅在确实需要时间时才显示加载器。因为当某些任务执行的时间少于某个延迟时 - 就像眨眼一样。

当我需要显示指标时,我会尝试下一步:

_mainWindow.BusyLoaderAsync(() => _resultViewModel.InitChart(lineChart, generalChart)).ConfigureAwait(false); // code which initialize WPF toolkit chart as example

问题 - 显示指示器时,它不旋转,它被冻结。我认为是因为 UI 线程被阻塞了。

是否可以检查阻塞 UI 线程,是否可以从上面的代码中检查它?

【问题讨论】:

  • 一个非常简单的检查,执行你的应用程序,等待你的加载器出现并且不旋转,暂停执行并检查线程面板,看看你的代码在哪里暂停,第一个线程应该是 UI线程。
  • @Gusman,是的,第一个线程是主线程,据我所知,在 WPF 中是 UI 线程
  • 这不是一个问题,我的意思是,如果你转到那个线程和调用堆栈,你会看到是什么阻塞了 UI 线程...
  • @Gusman,好吧,那么我无法检测到什么方法阻止 UI...在 CallStack 中我只能看到这个 i.imgur.com/BmrE9gL.png 而没有我的方法
  • 好像有什么东西阻止了控件的创建

标签: c# wpf multithreading


【解决方案1】:

BusyLoaderAsync(Action doWorkAction) 的实现存在问题,因为它依赖于doWorkAction 的实现。 如果doWorkAction 是同步的,比如:

BusyLoaderAsync(() =>
{
    Debug.WriteLine(" == Action Start ");
    Thread.Sleep(8000);
    Debug.WriteLine(" == Action End ");
});

然后它会锁定 UI 线程,直到 doWorkAction 结束之前 UI 中没有任何更新。结束后,tokenSource.Cancel(); 取消忙指示符,并且永远无法更新。

如果doWorkAction 是异步的,比如:

BusyLoaderAsync(async () =>
{
    Debug.WriteLine(" == Action Start ");
    await Task.Delay(8000);
    Debug.WriteLine(" == Action End ");
});

然后doWorkAction 运行得比StartSpinAsync 快,最终tokenSource.Cancel(); 在开始之前取消忙碌指示器,它也永远不会指示。

在同步情况下,如果doWorkAction可以在另一个线程中运行,BusyLoaderAsync可以如下

public async Task BusyLoaderAsync(Action doWorkAction)
{
    using (var tokenSource = new CancellationTokenSource())
    {
        var tasks = new[] { Task.Run(doWorkAction), Task.Delay(1000) };
        var result = Task.WaitAny(tasks);
        if (result == 1)
        {
            loadingPanel.IsLoading = true;
            await tasks[0];
            loadingPanel.IsLoading = false;
        }
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多