不少時候咱們須要作一個工做,在一個方法體裏面,讀取大數據綁定到UI界面,因爲長時間的讀取,讀取獨佔了線程域,致使界面一直處於假死狀態。例如,當應用程序開始讀取Web資源時,讀取的時效是由網絡鏈路的速度決定的,那麼在讀取的過程當中整個程序都必然處於一種等待狀態,這不是咱們想要看到的。那麼咱們有沒有一種機制既能解決效率問題同時能夠提供代碼的可用性呢?有人可能會說,咱們可使用線程池。線程真的是萬能的嗎?當處理大併發數據量時就能說明這個問題,線程池最大的併發量有限制,並且線程是極度佔用資源。
.NET 4.5下提供了一種異步方式,可以有效的避免效率瓶頸並加強應用程序的可維護性,這種機制相似於Node.js原理,這是一個徹底單線程的方法,當有數據傳遞過程時,它首先把數據交給異步處理,再完成後再經過回調返回數據集,咱們能夠理解爲c#版的閉包。它的最大做用是把原來的等待換成異步方式,把UI解放出來,由於UI的活動都是共用一個域的。
c#
先看看傳統模式下的無阻塞刷新,首先是XAML頁面文件網絡
<Grid> <Grid.RowDefinitions> <RowDefinition Height="*"></RowDefinition> <RowDefinition Height="30"></RowDefinition> </Grid.RowDefinitions> <ListView Grid.Row="0" Width="auto" Height="auto" Name="lvData" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"> <ListView.View> <GridView> <GridViewColumn Header="編號" DisplayMemberBinding="{Binding Path=Id}" Width="100" /> <GridViewColumn Header="隨機數" DisplayMemberBinding="{Binding Path=Code}" Width="160"/> </GridView> </ListView.View> </ListView> </Grid>
再新建一個ViewModel類,這個類的做用是爲咱們的MVVM模式提供一個數據源多線程
public class RandomViewModel { public RandomViewModel() { Model = new ObservableCollection<RandomData>(); } public ObservableCollection<RandomData> Model; public List<RandomData> dataList; public void FillData() { Model.Clear(); for (int i = 0; i < dataList.Count; i++) { Model.Add(dataList[i]); } } public void GetData() { dataList = new List<RandomData>(); Random random = new Random(); for (int i = 0; i < 20000; i++) { dataList.Add(new RandomData() { Id = i, Code = random.NextDouble() }); } } } public class RandomData { public int Id { get; set; } public double Code { get; set; } }
傳統模式下的無刷新調用,先在線程內部計算出須要綁定的數據,而後再使用Invoke填充Model.閉包
Thread thread = new Thread(p => { while (true) { ViewModel.GetData(); this.Dispatcher.Invoke(new Action<List<RandomData>>(result => { ViewModel.FillData(); }), ViewModel.dataList); Thread.Sleep(4000); } }); thread.IsBackground = true; thread.Start();
在代碼中嵌入線程函數,代碼是很分散的,同時也消耗大量資源。爲了讓咱們代碼儘量的作到內外同步執行。咱們使用async、await、Task併發
private async Task AsyncAccess() { while (true) { var getDataListTask = new Task(() => { Thread.Sleep(5000); ViewModel.GetData(); }); getDataListTask.Start(); await getDataListTask; var fillModelTask = Task.Factory.StartNew(() => { ViewModel.FillData(); }, CancellationToken.None, TaskCreationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()); await fillModelTask; } }
理論上說,Task.Factory.StartNew也是在線程池上執行,可表示異步操做並且更加通用,其中在Task前加上await使用成爲異步函數,否則UI界面仍會有卡頓的現象。
最後一個Invoke方法,須要經過SynchronizationContext.SetSynchronizationContext方法在多線程環境下設置SynchronizationContext.Current屬性,將線程控制域轉交至UI,設置當前SynchronizationContext相關的TaskScheduler,利用這個TaskScheduler,Task將經過當前SynchronizationContext來執行,實際上上述代碼在WPF下等效於調用Dispatcher.BeginInvoke方法。dom
.net 4.5下已默認封裝的相關類庫有異步
訪問Web | HttpClient , SyndicationClient |
使用文件 | StorageFile, StreamWriter, StreamReader,XmlReader |
使用圖像 | MediaCapture, BitmapEncoder, BitmapDecoder |
WCF程序開發 | Synchronous and Asynchronous Operations |
使用sockets | Socket |