简体   繁体   中英

Task.Run blocking main thread (frozen UI)

I'm trying to make an asynchronous call to load data to my grid. Problem is, operation is blocking the UI (running in the main thread) - and i don't know why. I'm trying to make it to fetch data in the background ... Here's the code:

Binding ViewModel class to DataContext of the main window:

<Window.DataContext>
    <vm:MainWindowViewModel WindowTitle="MVVM" BtnLoadText="LOAD DATA"/>
</Window.DataContext>

DataGrid with column binding to the collection property (PeopleList) in ViewModel class:

    <DataGrid AutoGenerateColumns="False" IsReadOnly="True" ItemsSource="{Binding Path=PeopleList, Mode=TwoWay}" Margin="5">
        <DataGrid.Columns>
            <DataGridTextColumn Header="First name" Binding="{Binding Path=FirstName}"/>
            <DataGridTextColumn Header="Last name" Binding="{Binding Path=LastName}"/>
            <DataGridTextColumn Header="Age" Binding="{Binding Path=Age}"/>
        </DataGrid.Columns>
    </DataGrid>
    <Button x:Name="btn_LoadData" Margin="5" Grid.Row="1" Content="{Binding Path=BtnLoadText}" Click="btn_LoadData_Click"/>

Code-Behind of the MainWindow - running asynchronous button click event:

public partial class MainWindow : Window
{
    private MainWindowViewModel mainWindowViewModel;
    public MainWindow()
    {
        InitializeComponent();
        mainWindowViewModel = (MainWindowViewModel)DataContext;
    }

    private async void btn_LoadData_Click(object sender, RoutedEventArgs e)
    {
        await mainWindowViewModel.LoadData();
    }
}

ViewModel class responsible for MainWindow:

class MainWindowViewModel
{
    public string WindowTitle { get; set; }
    public string BtnLoadText { get; set; }

    public ObservableCollection<Person> PeopleList { get; set; }

    private Database database = new Database();

    public MainWindowViewModel()
    {
        PeopleList = new ObservableCollection<Person>();
    }

    public async Task LoadData()
    {
        PeopleList.Clear();

        var result = await database.GetPeopleListLongOperationAsync();

        PeopleList.Add(result.First());
    }
}

As you see, i'm making an asynchronous call with LoadData method, which gets data from the database and adding it to ObservableCollection, which updates DataGrid (Binding from the DataGrid)

Database class, which "emulates" data fetching:

public class Database
{
    public IEnumerable<Person> GetPeopleListLongOperation()
    {
        // forcing "long" data load
        Thread.Sleep(5000);
        yield return new Person() { FirstName = "Name", LastName = "LastName", Age = new Random().Next(18, 40) };
    }

    public Task<IEnumerable<Person>> GetPeopleListLongOperationAsync()
    {
        return Task.Run<IEnumerable<Person>>(() =>
        {
            return GetPeopleListLongOperation();
        });
    }
}

I'm using Task.Run to fetch data in background thread. The problem is - it is running in Main Thread and it's blocking the UI.

Any suggestions? I'm not sure anymore, if i understand async operations correctly ...

EDIT

Changing Task result type from IEnumerable to List made it work. Can someone explain me why?

    public Task<List<Person>> GetPeopleListLongOperationAsync()
    {
        return Task.Run<List<Person>>(() =>
        {
            return GetPeopleListLongOperation().ToList();
        });
    }

Forget about the threading complication for now, and just take this code:

public IEnumerable<Person> GetPeopleListLongOperation()
{
    // forcing "long" data load
    Thread.Sleep(5000);
    yield return new Person();
}

When you call GetPeopleListLongOperation() it will return immediately , it won't wait 5 seconds. This is because iterator blocks like this are lazily evaluated; Thread.Sleep(5000) will only be called when you enumerate through the sequence.

Now you understand that, you should see that you 'fix' works because ToList will enumerate the sequence to completion while still on the thread pool thread and return the cached results. Without this you were enumerating the sequence on the UI thread on the call to result.First() and blocking it.

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