简体   繁体   中英

UI thread is blocked

In MainWindow I have async method which displays busy Indicator

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);
        }
    }

Loading Panel looks like :

<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>

Control Loading Panel has 2 additional methods :

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);
    }

We want to show loader only if it realy takes time. Cause when some task executes less than some delay - it is like blink.

When I need to show indicator I try next :

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

Problem - when indicator is shown, it doesn't spining, it is freezed. I think it is because UI thread is blocked.

Is it possible to check what block UI thread and is it possible to check it from code above?

It is problematic implementation of BusyLoaderAsync(Action doWorkAction) because it depends on doWorkAction implementation. If doWorkAction is synchronous like:

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

then it locks UI thread, and nothing updates in UI until doWorkAction ends. After it ends, tokenSource.Cancel(); cancels the busy indicator, and it can never updates.

If doWorkAction is asynchronous like:

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

then doWorkAction runs faster then StartSpinAsync and eventually tokenSource.Cancel(); cancels the busy indicator before it starts, and it also never indicates.

In synchronous case, if doWorkAction can run in another thread, BusyLoaderAsync can be as following

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;
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM