2017-04-01 166 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