时间:2015年04月15日 | 作者 : aaronyang | 分类 :WPF4.5系列 | 浏览: 10次 | 评论0人
文章摘要:
1. 通过简单DEMO.让读者理解Task和Task<T> 学习过程中,掌握async和await
2. 理解同步和异步的执行
3. Task.Factory.StartNew()的替代
4. WPF中传统方式async的应用,让界面不卡
5. Ay 自编的无任何组件的 快速的MVVM应用,Async版本,实战DEMO
以下DEMO 以vs2013开发 .Net Framework4.5+ 控制台程序为主
前言: async标记 await异步执行方法,可返回值可不返回,返回值则async Task<T> 不返回则async Task
实验1:
使用Task.Delay(TimeSpan)担任耗时操作
第一课,理解什么时候Task和Task<T>,同步与异步
返现目前是同步的执行,而且AddNum画了绿色的线条,我们使用 .Result()拿到Task返回的值
Ok,缺少await运算符,我们使用Task.Delay延迟下,让它执行一个异步操作,此时AddNum方法没有了绿色,我们鼠标移到Delay方法上
返回的是Task,并不是Task<T> 所以此方法没有返回值.
(个人理解:如果 async 修饰的方法体中没有await操作等于没有加async的效果一样的,就是个普通的方法,只不过已经告诉编译器这可能是个异步方法,如果被await关键字调用的操作就是名副其实的异步方法了.
一个async方法有很多同步的方法组成,你的写法,最终是编译器把你的改成了老式的异步写法) 不理解没关系,先看完文章
我们注释刚刚的延迟操作,把操作放到另一个方法中-操作移出来:
=============潇洒的版权线==========www.ayjs.net===== Aaronyang ========= AY =========== 安徽 六安 杨洋 ========== 未经允许不许转载 =========
这里没有用async方法的Result拿返回值了,因为await修饰了async的方法(个人理解:async表示该方法可以被异步的调用)
效果如下:
OK,接下来,我们再增加一个3倍操作的方法
await保证了代码的执行顺序,让代码不会出现一会3倍操作..一会2倍操作.而是很有顺序的执行
效果图:
ok,接下来我们增加一个在异步方法体里面console.writeline
static async Task AddNumDouble2(int num) { //一个耗时的计算 await Task.Delay(TimeSpan.FromSeconds(1)); Console.WriteLine("AddNumDouble2方法中:" + num * 2); }
修改AddNum方法如下:
static async Task<int> AddNum() { int a=0; for (int i = 0; i < 4; i++) { //var nextDelay = TimeSpan.FromSeconds(1); //await Task.Delay(nextDelay); //var result = await AddNumDouble(i); //Console.WriteLine("2倍操作:"+result); //var result2 = await AddNumThree(i); //Console.WriteLine("3倍操作:" + result2); await AddNumDouble2(i); a++; } return a; }
效果图:
接下来我们去掉await关键字执行,让代码没有顺序
static async Task<int> AddNum() { int a=0; for (int i = 0; i < 4; i++) { //var nextDelay = TimeSpan.FromSeconds(1); //await Task.Delay(nextDelay); //var result = await AddNumDouble(i); //Console.WriteLine("2倍操作:"+result); //var result2 = await AddNumThree(i); //Console.WriteLine("3倍操作:" + result2); //await AddNumDouble2(i); AddNumDouble2(i); Console.WriteLine("www.ayjs.net开心就好"+i+"次"); a++; } return a; }
效果图:
此时你发现AddNumDouble2还没执行完, Console.WriteLine("www.ayjs.net开心就好"+i+"次");代码就执行了,而且for循环也执行完了,而且AddNumDouble2中的内容输出也没有顺序,是0642,而不是0246
实验2(简单的实战)
新建一个WPF程序或者winform程序,这里我用wpf.
界面很简单,一个文本框,一个按钮,按钮的Click事件如下:
第一种代码:
public int SyncAddNumDouble(int num) { //一个耗时的计算 Thread.Sleep(1000); return num * 2; } private void Button_Click(object sender, RoutedEventArgs e) { int sum = 0; for (int i = 0; i < 4; i++) { var result=SyncAddNumDouble(i); sum += result; } txtResult.Text = sum.ToString(); }
效果如下:你会发现窗体在后台执行耗时操作时候,根本卡死,执行完后,才能响应.
我们继续修改代码:
public async Task<int> SyncAddNumDouble(int num) { //一个耗时的计算 await Task.Delay(1000); return num * 2; } public async Task ImportData() { int sum = 0; for (int i = 0; i < 4; i++) { var result = await SyncAddNumDouble(i); sum += result; } txtResult.Text = sum.ToString(); } private void Button_Click(object sender, RoutedEventArgs e) { ImportData(); }
效果图:发现在点击了导入之后,UI线程没有被阻塞,还是可以想干嘛就干嘛,这样才是我们的想要的,这就是异步的效果
当然不是说backgroundworker,Dispatcher方案就不是不可以了,都可以的.
如果你要进行的操作不支持await修饰怎么办? 使用Task.Factory.StartNew()就可以了,我们继续修改代码
public int SyncAddNumDouble(object num1) { int num = (int)num1; //一个耗时的计算 Thread.Sleep(1000); return num * 2; } public async Task ImportData() { int sum = 0; for (int i = 0; i < 4; i++) { var result = await Task.Factory.StartNew((Func<object, int>)SyncAddNumDouble, i); sum += result; } txtResult.Text = sum.ToString(); } private void Button_Click(object sender, RoutedEventArgs e) { ImportData(); }
效果同上,不附加图片了.
让DEMO更佳完善,Command的新的配合思路
这里首先我们需要一个辅助类
namespace Ay.Framework.WPF.Shared { using System; using System.Windows.Input; public class DelegateCommand : ICommand { private readonly Action execute; private readonly Func<bool> canExecute; public DelegateCommand(Action execute) : this(execute, null) { } public DelegateCommand(Action execute, Func<bool> canExecute) { if (execute == null) { throw new ArgumentNullException("execute"); } this.execute = execute; this.canExecute = canExecute; } public event EventHandler CanExecuteChanged { add { if (canExecute != null) { CommandManager.RequerySuggested += value; } } remove { if (canExecute != null) { CommandManager.RequerySuggested -= value; } } } public void RaiseCanExecuteChanged() { CommandManager.InvalidateRequerySuggested(); } public bool CanExecute(object parameter) { return canExecute == null ? true : canExecute(); } public void Execute(object parameter) { execute(); } } }
我都是手动MVVM的,新建一个ViewModelBase
public class ViewModelBase : INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged(string property) { var handler = PropertyChanged; if (handler != null) { handler(this, new PropertyChangedEventArgs(property)); } } }
OK,准备就绪,我们写一个MainWindow对应的ViewModel类,也就是MainViewModel
public class MainViewModel : ViewModelBase { }
接下来在MainViewModel中我们增加一个
public ICommand ImportDataCommand { get; set; }
在构造函数中,绑定对应的实现,由于对应的ViewModel无法拿到window的控件,为了让最终值的文本显示,我们需要添加一个自动通知的属性
private string importResult; public string ImportResult { get { return this.importResult; } set { this.importResult = value; OnPropertyChanged("ImportResult"); } }
把刚刚第一个版本的操作代码迁移过来,并修改那个文本框的Text代码,换成 ImportResult的赋值,并把Task的返回值换成void
public async Task<int> SyncAddNumDouble(int num) { //一个耗时的计算 await Task.Delay(1000); return num * 2; } private async void ImportData() { int sum = 0; for (int i = 0; i < 4; i++) { var result = await SyncAddNumDouble(i); sum += result; } ImportResult = sum.ToString(); }
然后构造函数,绑定这个异步方法
public MainViewModel() { this.ImportDataCommand = new DelegateCommand(this.ImportData); }
OK,目前为止ViewModel算写完了,很简单吧,我们打开MainWindow,按照MVVM的写法,都是讲ViewModel赋予窗体的DataContext
public MainWindow() { InitializeComponent(); this.DataContext = new MainViewModel(); }
而窗体对应的xaml中 都是binding对应ViewModel能绑定的东西,绑定吧!!!
<Window x:Class="WpfApplication1.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" WindowStartupLocation="CenterScreen" Title="www.ayjs.net aaronyang" Height="350" Width="525"> <Grid> <Button Content="导入数据" HorizontalAlignment="Left" Command="{Binding ImportDataCommand}" Margin="133,32,0,0" VerticalAlignment="Top" Width="190" Height="26"/> <TextBox x:Name="txtResult" HorizontalAlignment="Left" Height="24" Margin="133,87,0,0" TextWrapping="Wrap" Text="{Binding ImportResult}" VerticalAlignment="Top" Width="210"/> </Grid> </Window>
运行吧! 效果一样,主窗体没有阻塞,效果照样能运行.
好了,今天的内容现讲到这里
=============潇洒的版权线==========www.ayjs.net===== Aaronyang ========= AY =========== 安徽 六安 杨洋 ========== 未经允许不许转载 =========
-------------------小小的推荐,作者的肯定,读者的支持。推不推荐不重要,重要的是希望大家能把WPF推广出去,别让这么好的技术消失了,求求了,让我们为WPF技术做一份贡献。-----------------