Task.Run阻塞主线程(冻结的UI)

时间:2017-04-01 10:13:52

标签: c# wpf multithreading asynchronous task

我试图进行异步调用以将数据加载到网格中。问题是,操作阻止了UI(在主线程中运行) - 我不知道为什么。我试图让它在后台获取数据......以下是代码:

将ViewModel类绑定到主窗口的DataContext:

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

DataGrid,其列绑定到ViewModel类中的集合属性(PeopleList):

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

MainWindow的代码隐藏 - 运行异步按钮单击事件:

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

负责MainWindow的ViewModel类:

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

如您所见,我正在使用LoadData方法进行异步调用,该方法从数据库获取数据并将其添加到ObservableCollection,后者更新DataGrid(从DataGrid绑定)

数据库类,&#34;模拟&#34;数据获取:

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

我使用Task.Run在后台线程中获取数据。问题是 - 它在主线程中运行并且它阻止了UI。

有什么建议吗?如果我理解正确的异步操作,我不确定了......

修改

将任务结果类型从IEnumerable更改为List使其工作。有人能解释我为什么吗?

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

1 个答案:

答案 0 :(得分:5)

暂时忘掉线程并发症,只需使用此代码:

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

当您致电GetPeopleListLongOperation()时,它会立即返回 ,它不会等待5秒。这是因为像这样的迭代器块被懒惰地评估了;只有在您按顺序枚举时才会调用Thread.Sleep(5000)

现在你明白了,你应该看到你正在修复&#39;因为ToList将在线程池线程上枚举完成序列并返回缓存结果,因此可以正常工作。如果没有这个,你在result.First()的调用中枚举UI线程上的序列并阻止它。