zoukankan      html  css  js  c++  java
  • WPF MVVM模式下的无阻塞刷新探讨

    很多时候我们需要做一个工作,在一个方法体里面,读取大数据绑定到UI界面,由于长时间的读取,读取独占了线程域,导致界面一直处于假死状态。例如,当应用程序开始读取Web资源时,读取的时效是由网络链路的速度决定的,那么在读取的过程中整个程序都必然处于一种等待状态,这不是我们想要看到的。那么我们有没有一种机制既能解决效率问题同时可以提供代码的可用性呢?有人可能会说,我们可以使用线程池。线程真的是万能的吗?当处理大并发数据量时就能说明这个问题,线程池最大的并发量有限制,而且线程是极度占用资源。

    .NET 4.5下提供了一种异步方式,能够有效的避免效率瓶颈并增强应用程序的可维护性,这种机制类似于Node.js原理,这是一个完全单线程的方法,当有数据传递过程时,它首先把数据交给异步处理,再完成后再通过回调返回数据集,我们可以理解为c#版的闭包。它的最大作用是把原来的等待换成异步方式,把UI解放出来,因为UI的活动都是共用一个域的。

    先看看传统模式下的无阻塞刷新,首先是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>
    View Code


    再新建一个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; }
        }
    View Code

    传统模式下的无刷新调用,先在线程内部计算出需要绑定的数据,然后再使用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();
    View Code

    在代码中嵌入线程函数,代码是很分散的,同时也消耗大量资源。为了让我们代码尽可能的做到内外同步执行。我们使用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;
                }
    
            }
    View Code

    理论上说,Task.Factory.StartNew也是在线程池上执行,可表示异步操作而且更加通用,其中在Task前加上await使用成为异步函数,不然UI界面仍会有卡顿的现象。
    最后一个Invoke方法,需要通过SynchronizationContext.SetSynchronizationContext方法在多线程环境下设置SynchronizationContext.Current属性,将线程控制域转交至UI,设置当前SynchronizationContext相关的TaskScheduler,利用这个TaskScheduler,Task将通过当前SynchronizationContext来执行,实际上上述代码在WPF下等效于调用Dispatcher.BeginInvoke方法。

    .net 4.5下已默认封装的相关类库有

    访问Web HttpClient , SyndicationClient
    使用文件 StorageFile, StreamWriter, StreamReader,XmlReader
    使用图像 MediaCapture, BitmapEncoder, BitmapDecoder
    WCF程序开发 Synchronous and Asynchronous Operations
    使用sockets Socket
  • 相关阅读:
    QT学习笔记2
    QT学习笔记1
    图像中区域生长算法的详解和实现
    图像识别中的深度学习 转
    Appium基础环境搭建(windows)---基于python
    selenium实现文件上传方法汇总(AutoIt、win32GUI、sengkeys)---基于python
    selenium中的三种等待方式(显示等待WebDriverWait()、隐式等待implicitly()、强制等待sleep())---基于python
    水仙花数---基于python
    九九乘法表实现---基于python
    selenium元素定位不到之iframe---基于python
  • 原文地址:https://www.cnblogs.com/yzp12sina/p/3512265.html
Copyright © 2011-2022 走看看