2017-04-01 110 views
4

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

绑定视图模型类的DataContext主窗口:

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

在DataGrid中列绑定到集合属性( PeopleList)在ViewModel类:在主窗口的

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

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

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 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,这将更新数据网格(从绑定数据网格)

数据库类,其中“模拟”的数据抓取:

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获取在后台线程数据。问题是 - 它在Main Thread中运行,并且阻塞了UI。

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

编辑

自IEnumerable更改任务结果类型列出使其正常工作。有人可以解释我为什么吗?

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

回答

5

忘掉线程并发症现在,正好利用这个代码:

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

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

现在你明白了,你应该看到你'修复'的作品,因为ToList将枚举顺序完成,同时仍然在线程池线程上并返回缓存的结果。没有这个,你在调用result.First()时在UI线程上枚举了序列并将其阻塞。

+0

谢谢。我知道关于IEnumerable集合的延迟加载,但我不知道它是这样工作的。 – asiesom