(10)C#异步编程
参考博客:
https://www.cnblogs.com/yswenli/p/11987377.html
https://www.cnblogs.com/wisdomqq/archive/2012/03/26/2412349.html
https://www.cnblogs.com/scy251147/archive/2012/03/03/2378477.html
https://www.cnblogs.com/zhaoshujie/p/9634136.html
https://www.cnblogs.com/zhaoshujie/p/11192036.html
1、异步编程的相关概念
1.1、 并发、并行、异步、多线程的概念
- 并发:对于某个系统,需要同时处理同个用户请求,多个用户请求的过程和处理多个用户请求的过程就叫并发。
- 并行:同一时刻有多件事情同时被执行,这些指令可能执行于同一CPU的多核上,或者多个CPU上,或者多个物理主机甚至多个网络中。
- 异步:和同步相对,同步是顺序执行,而异步是彼此独立,在等待某个事件的过程中继续做自己的事,不要等待这一事件完成后再工作。异步和并行并不是完全互斥的,有重合的情况;两种概念只是侧重点不同,
- 多线程:一般指同一进程中多个线程协作运行。
2、直接使用多线程的异步方式
直接开两个线程,异步执行各自的操作。最原始的方法,不推荐直接操作线程。
public static void Main()
{ Thread writeThread = new Thread(new ThreadStart(WriteWapper)); Thread doSomethingThread = new Thread(new ParameterizedThreadStart(DoSomethingWapper)); ClosureClass closure = new ClosureClass(); writeThread.Start(); doSomethingThread.Start(closure);//闭包对象,用于变量穿越 writeThread.Join(); doSomethingThread.Join(); Console.WriteLine(closure.Result); } //将方法包装成适于线程调用的签名 private static void WriteWapper() { using (var fs = new FileStream("Data.dat", FileMode.OpenOrCreate, FileAccess.Write, FileShare.None, 4096)) { fs.Write(new byte[100], 0, 100); } } //将方法包装成适于线程调用的签名 static void DoSomethingWapper(object state) { ClosureClass closure = state as ClosureClass; var result = DoSomething(); if (closure != null) { closure.Result = result; } } static string DoSomething() { Thread.Sleep(2000); return "Finished"; } //闭包辅助类,用于存储在方法间传递内部变量与参数 class ClosureClass { //存储方法返回值 public string Result { get; set; } }
3、 基于计时器的异步方式
计时器提供了一种重复运行的异步方式,当每次计时器到期之后,就会在线程池中开启一个线程,运行对应的回调方法。Time类中一种构造函数如下
//callback:到期之后的回调方法 //state:传给回调方法的参数 //dueTime:首次被调用的时间 //period:间隔时间 public Timer(TimerCallback callback, object state, int dueTime, int period);
示例:
namespace Timers { class Program { int TimesCalled = 0; void Display( object state ) { Console.WriteLine( "{0} {1}", (string) state, ++TimesCalled ); } static void Main() { Program p = new Program(); //2000代表2s后第一次调用;1000代表每1s重复一次 Timer myTimer = new Timer( p.Display, "Processing timer event", 2000, 1000 ); Console.WriteLine( "Timer started." ); Console.ReadLine(); } } }
4、基于BackgroundWorker的异步方式
4.1、 BackgroundWorker类介绍
BackgroundWorker类会新建另一个线程,在后台持续运行某项工作,并持续和主线程进行通信。BackgroundWorker类结果如下图所示:
1)四个属性
- WorkerReportsProgress(R/W):表示后台线程的进度信息是否汇报给主线程。
- WorkerSupportsCacllation(R/W) :表示后台线程是否支持在主线程中取消。
- IsBusy(R):表示后端线程是否忙(是否完成)
- CancellationPending(R):表示是否在主线程中取消了后台线程。
2)三个方法
- RunWorkerAsync()
(1)开始执行一个后台操作。主线程调用该方法后,将触发BackgroundWorker.DoWork事件,系统重启一个后台线程执行DoWork事件中的代码。
(2)该方法还有一个带参数的重载方法:RunWorkerAsync(Object)。该方法允许传递一个Object类型的参数到后台操作中,并且可以通过DoWork事件的DoWorkEventArgs.Argument属性将该参数提取出来。
(3)当BackgroundWorker的IsBusy属性为True时,调用该方法将引发InvalidOperationException异常。 - ReportProgress(Int percentProgress)
(1)报告操作进度。调用该方法后,将触发BackgroundWorker. ProgressChanged事件。另外,该方法包含了一个int类型的参数percentProgress,用来表示当前异步操作所执行的进度百分比。
(2)该方法还有一个重载方法:ReportProgress(Int percentProgress, Object userState)。允许传递一个Object对象到 ProgressChanged事件中,并且可以通过ProgressChanged事件的ProgressChangedEventArgs.UserState属性取得参数值。
(3)调用该方法之前需确保WorkerReportsProgress属性值为True,否则将引发InvalidOperationException异常。 - CancelAsync()
(1)请求取消当前正在执行的异步操作,调用该方法将使BackgroundWorker.CancellationPending属性设置为True,在其他地方可以读出此属性。
(2)调用该方法之前需确保WorkerSupportsCacllation属性值为True
3)三个事件(由主线程指定)
- DoWork
(1)用于承载异步操作。当调用BackgroundWorker.RunWorkerAsync()时触发。
(2)需要注意的是,由于DoWork事件内部的代码运行在非UI线程之上,所以在DoWork事件内部应避免于用户界面交互,而于用户界面交互的操作应放置在ProgressChanged和RunWorkerCompleted事件中。
(3)对应委托的参数,sender:表示调用委托的对象(即BackgroundWorker对象);DoworkEventArgs.Argument:委托调用的参数;DoWorkEventArgs.Result:委托调用后的结果。DoWorkEventArgs.Cancel:主线程是否取消后台线程//DoWork对应委托 public delegate void DoWorkEventHandler(object sender, DoWorkEventArgs e); //DoWorkEventArgs public class DoWorkEventArgs : CancelEventArgs { public DoWorkEventArgs(object argument); public object Argument { get; } public object Result { get; set; } }
-
ProgressChanged
(1)当调用BackgroundWorker.ReportProgress(int percentProgress)方式时触发该事件。
(2)该事件的ProgressChangedEventArgs.ProgressPercentage属性可以接收来自ReportProgress方法传递的percentProgress参数值,ProgressChangedEventArgs.UserState属性可以接收来自ReportProgress方法传递的userState参数。//ProgressChanged 对应委托 public delegate void ProgressChangedEventHandler(object sender, ProgressChangedEventArgs e); //ProgressChangedEventArgs public class ProgressChangedEventArgs : EventArgs { public ProgressChangedEventArgs(int progressPercentage, object userState); public int ProgressPercentage { get; } public object UserState { get; } }
-
RunWorkerCompleted
(1)异步操作完成或取消时执行的操作,当调用DoWork事件执行完成时触发。
(2)该事件的RunWorkerCompletedEventArgs参数包含三个常用的属性Error,Cancelled,Result。其中,Error表示在执行异步操作期间发生的错误;Cancelled用于判断用户是否取消了异步操作;Result属性接收来自DoWork事件的DoWorkEventArgs参数的
Result属性值,可用于传递异步操作的执行结果。//RunWorkerCompleted 对应委托 public delegate void RunWorkerCompletedEventHandler(object sender, RunWorkerCompletedEventArgs e); //RunWorkerCompletedEventArgs public class RunWorkerCompletedEventArgs : AsyncCompletedEventArgs { public RunWorkerCompletedEventArgs(object result, Exception error, bool cancelled); public object Result { get; } public object UserState { get; } } //AsyncCompletedEventArgs public class AsyncCompletedEventArgs : EventArgs { public AsyncCompletedEventArgs(); public AsyncCompletedEventArgs(Exception error, bool cancelled, object userState); public bool Cancelled { get; } public Exception Error { get; } public object UserState { get; } protected void RaiseExceptionIfNecessary(); }
4.2 BackGround类示例
如下程序,点击按钮后,后台线程启动执行自己的操作,每隔半秒向主线程汇报进度;取消或成功执行后弹对应框。
<Window x:Class="SimpleWorker.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="MainWindow" Height="150 " Width="250"> <StackPanel> <ProgressBar Name="progressBar" Height="20" Width="200" Margin="10"/> <Button Name="btnProcess" Width="100" Click="btnProcess_Click" Margin="5">Process</Button> <Button Name="btnCancel" Width="100" Click="btnCancel_Click" Margin="5">Cancel</Button> </StackPanel> </Window>
namespace SimpleWorker { public partial class MainWindow : Window { BackgroundWorker bgWorker = new BackgroundWorker(); public MainWindow() { InitializeComponent(); // Set BackgroundWorker properties bgWorker.WorkerReportsProgress = true; bgWorker.WorkerSupportsCancellation = true; // Connect handlers to BackgroundWorker object. bgWorker.DoWork += DoWork_Handler; bgWorker.ProgressChanged += ProgressChanged_Handler; bgWorker.RunWorkerCompleted += RunWorkerCompleted_Handler; } private void btnProcess_Click( object sender, RoutedEventArgs e ) { if ( !bgWorker.IsBusy ) bgWorker.RunWorkerAsync(); } private void ProgressChanged_Handler( object sender, ProgressChangedEventArgs args ) { progressBar.Value = args.ProgressPercentage; } private void DoWork_Handler( object sender, DoWorkEventArgs args ) { BackgroundWorker worker = sender as BackgroundWorker; for ( int i = 1; i <= 10; i++ ) { if ( worker.CancellationPending ) { args.Cancel = true; break; } else { worker.ReportProgress( i * 10 ); Thread.Sleep( 500 ); } } } private void RunWorkerCompleted_Handler( object sender, RunWorkerCompletedEventArgs args ) { progressBar.Value = 0; if ( args.Cancelled ) MessageBox.Show( "Process was cancelled.", "Process Cancelled" ); else MessageBox.Show( "Process completed normally.", "Process Completed" ); } private void btnCancel_Click( object sender, RoutedEventArgs e ) { bgWorker.CancelAsync(); } } }
5、基于BeginInvoke和EndInvoke的异步方式
对于委托而言,如果列表容器内只有一个方法(此时称为引用方法),可以通过委托类的两个方法BenginIvoke和EndInvoke来异步执行引用方法。
- BeginInvoke
(1)输入参数:引用方法对应的参数(可以有ref和out参数);Callback参数;state参数(设置BeginInvoke返回参数和回调函数中IAsyncResult.AyncState);
(2)返回参数:IAsyncResult对象(记录异步方法当前状态)
(3)调用时,它会在线程池中获取一个独立的线程,执行对应的方法,并立即返回到主线程。 - EndInvoke
(1)输入参数:BeginInvoke返回的IAsyncResult对象;返回参数:引用方法的执行结果。
(2)调用时,清理线程状态并释放资源,如果此时BeginInvoke对应的操作还未完成,则在主线程堵塞等待。 - AsyncResult类
(1)IsCompleted:表示引用方法是否执行完成。
(2)AyncState:对应BeginInvoke方法的state参数
(3)AsyncDelegate:记录委托本身。

5.1 轮询和一直等待模式
delegate long MyDel( int first, int second ); class Program { static long Sum( int x, int y ) { Console.WriteLine( " Inside Sum" ); Thread.Sleep( 100 ); return x + y; } static void Main() { MyDel del = new MyDel( Sum ); IAsyncResult iar = del.BeginInvoke( 3, 5, null, null ); // Start async. Console.WriteLine( "After BeginInvoke" ); //轮询模式,通过IsCompleted与引用方法线程一直通信,得知是否完成。 while ( !iar.IsCompleted ) { Console.WriteLine( "Not Done" ); for ( long i = 0; i < 10000000; i++ ) ; } //去掉上面的轮询,这里就是一直等待模式,会堵塞主线 long result = del.EndInvoke( iar ); Console.WriteLine( "Result: {0}", result ); } }
5.2 回调模式
回调模式的不同在于,在调用BeginInvoke时指定一个回调方法,引用方法结束时需在最后调用EndInvoke,此后系统就会在主线程中自动调用这个回调方法。回调方法签名如下所示;
需要特别注意的是,如果引用方法还未结束,主线程已经结束并退出了,此时异步线程永远回不来了。
回调方法的输入参数和BeginInvoke的返回参数,是同一个对象。
public delegate void AsyncCallback(IAsyncResult ar);
delegate long MyDel( int first, int second ); class Program { static long Sum( int x, int y ) { Console.WriteLine( " Inside Sum" ); Thread.Sleep( 100 ); return x + y; } static void CallWhenDone( IAsyncResult iar ) { Console.WriteLine( " Inside CallWhenDone." ); AsyncResult ar = (AsyncResult) iar; MyDel del = (MyDel) ar.AsyncDelegate; long result = del.EndInvoke( iar ); Console.WriteLine( " The result is: {0}.", result ); } static void Main() { MyDel del = new MyDel( Sum ); Console.WriteLine( "Before BeginInvoke" ); IAsyncResult iar = del.BeginInvoke( 3, 5, CallWhenDone, null ); Console.WriteLine( "Doing more work in Main." ); Thread.Sleep( 500 ); Console.WriteLine( "Done with Main. Exiting." ); } }
6、基于Task的异步方式
Task是基于任何的异步模式的中心组件之一,表示异步方式的单个操作。
class MyClass {public async Task DoWorkAsync() { Func<int> ten = new Func<int>( ()=> 10 ); int a = await Task.Run( ten ); Console.WriteLine( "{0} {1} {2}", a, b, c ); }
static void Main() { Task t = ( new MyClass() ).DoWorkAsync(); t.Wait(); } }
1)异步方法
- 以aync修饰符开始;包含一个或多个await表达式(表示需异步完成的任务);
返回类型有三种:void,Task(需异步完成的任务),Task<T>(除task外,还包含异步方法的返回值Task<T>.Result);只有Task<T>与异步方法的返回类型有关 - await异步处理的任务(如下示例)
await的之前的部分:是调用异步方法时同步执行的;
await task部分:await是指等待任务执行完成;如果task任务是不重开线程执行,则await堵塞直到任务完成。如果重开线程,则主线程执行其他操作,异步线程执行task。
await之后的部分:如果执行任务时重开线程了,在控制台程序中,则在异步线程中执行;如果在UI等程序中,则在调用线程(主线程)。如果没有重开线程,则都在主线程中执行。 - 当主线程用到了Task<T>.Result或Task.wait,会堵塞直到异步方法执行完成。
- 异步方法的Lambda表达式:加上async关键字即可,其他和普通方法的ladmbda表达式一致。
- 异步方法的参数可以是任意类型和数量,但不能包含out和ref参数。
示例1:task任务不重开线程的情况。通过输出结果可知,await会堵塞主线程,直至任务完成。(并没有达到异步的效果)
class Program { static void Main() { Console.WriteLine("MainStart:线程Id:【{0}】", Thread.CurrentThread.ManagedThreadId); DoRun(); Console.WriteLine("MainEnd:线程Id:【{0}】", Thread.CurrentThread.ManagedThreadId); Console.ReadLine(); } public async static void DoRun() { Console.WriteLine("DoRunStart:线程Id:【{0}】", Thread.CurrentThread.ManagedThreadId); await MethodName(); Console.WriteLine("DoRunEnd:线程Id:【{0}】", Thread.CurrentThread.ManagedThreadId); } static Task MethodName() { Console.WriteLine("MethodNameStart:Task运行在线程id为{0}的线程上。是否是线程池中线程?:{1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); //不会重开线程 Thread.Sleep(2000); Console.WriteLine("MethodNameEnd:Task运行在线程id为{0}的线程上。是否是线程池中线程?:{1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); return Task.FromResult<object>(null); } }//输出结果为: //MainStart:线程Id:【1】 //DoRunStart:线程Id:【1】 //MethodNameStart:Task运行在线程id为1的线程上。是否是线程池中线程?:False //MethodNameEnd:Task运行在线程id为1的线程上。是否是线程池中线程?:False //DoRunEnd:线程Id:【1】 //MainEnd:线程Id:【1】
示例2:task任务重开线程的情况(控制台程序)。由输出结果可知:
第一个await,由于MethodName任务里重开线程了,因此会异步等待MethodName任务方法完成。主线程会继续执行。
第二个await,由于Task.Delay重开线程了,因此会异步等待Task.Delay的完成。
等Task.Delay完成后,会在异步线程中执行后续语句;等MethodName完成后,会在异步线程中执行后续语句。
class Program { static void Main() { Console.WriteLine("MainStart:线程Id:【{0}】", Thread.CurrentThread.ManagedThreadId); DoRun(); Console.WriteLine("MainEnd:线程Id:【{0}】", Thread.CurrentThread.ManagedThreadId); Console.ReadLine(); } public async static void DoRun() { Console.WriteLine("DoRunStart:线程Id:【{0}】", Thread.CurrentThread.ManagedThreadId); await MethodName(); Console.WriteLine("DoRunEnd:线程Id:【{0}】", Thread.CurrentThread.ManagedThreadId); } static async Task MethodName() { Console.WriteLine("MethodNameStart:Task运行在线程id为{0}的线程上。是否是线程池中线程?:{1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); //task.delay会在线程池中取一个线程执行任务。 await Task.Delay(2000); Console.WriteLine("MethodNameEnd:Task运行在线程id为{0}的线程上。是否是线程池中线程?:{1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); } } //输出结果为: //MainStart:线程Id:【1】 //DoRunStart:线程Id:【1】 //MethodNameStart:Task运行在线程id为1的线程上。是否是线程池中线程?:False //MainEnd:线程Id:【1】 //MethodNameEnd:Task运行在线程id为4的线程上。是否是线程池中线程?:True //DoRunEnd:线程Id:【4】
示例3:Task.Run一定会重开一个线程执行任务。
class Program { static void Main() { Console.WriteLine("MainStart:线程Id:【{0}】", Thread.CurrentThread.ManagedThreadId); DoRun(); Console.WriteLine("MainEnd:线程Id:【{0}】", Thread.CurrentThread.ManagedThreadId); Console.ReadLine(); } public async static void DoRun() { Console.WriteLine("MethodNameStart:Task运行在线程id为{0}的线程上。是否是线程池中线程?:{1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); await Task.Run(()=> Thread.Sleep(2000)); Console.WriteLine("MethodNameEnd:Task运行在线程id为{0}的线程上。是否是线程池中线程?:{1}", Thread.CurrentThread.ManagedThreadId, Thread.CurrentThread.IsThreadPoolThread); } } //输出结果为: //MainStart:线程Id:【1】 //MethodNameStart:Task运行在线程id为1的线程上。是否是线程池中线程?:False //MainEnd:线程Id:【1】 //MethodNameEnd:Task运行在线程id为3的线程上。是否是线程池中线程?:True
2)await Task 表达式
- await表达式指定一个异步执行的任务(空闲对象);task实际上就是一个awaitable类型实例。
- task可以有两种方式获得:
(1) .NET预定义很多返回task<T>的方法,可以直接用于await后面,如DownloadStringTaskAync方法。
(2) 使用Task.Run创建Task或Task<T>,这种方式系统会新建线程执行task。下图为Task.Run的所有重载类型及对应委托参数。
![]()
3)取消异步操作
CancellationTokenSource实例调用cancel命令,会将其CancellationToken属性的IsCancellationRequested属性设置为true,拥有CancellationToken属性的方法可以实时读到IsCancellationRequested的值,并作出相应变化。
class Program { static void Main() { CancellationTokenSource cts = new CancellationTokenSource(); CancellationToken token = cts.Token; Task t = RunAsync( token ); Thread.Sleep( 3000 ); cts.Cancel(); t.Wait(); Console.WriteLine( "Was Cancelled: {0}", token.IsCancellationRequested ); } public async Task RunAsync( CancellationToken ct ) { if ( ct.IsCancellationRequested ) return; await Task.Run( () => CycleMethod( ct ) ); } void CycleMethod( CancellationToken ct ) { Console.WriteLine( "Starting CycleMethod" ); const int max = 5; for ( int i=0; i < max; i++ ) { // 实时读取IsCancellationRequested属性(并不复杂,类似指针的方法) if ( ct.IsCancellationRequested ) return; Thread.Sleep( 1000 ); Console.WriteLine( " {0} of {1} iterations completed", i + 1, max ); } } }
4)异常处理和异步方法
如下代码,虽然在task中抛出了异常,但返回的task的Status为RanToCompletion;IsFaulted为false;这是因为task抛出的异常被catch住了。
class Program { static void Main( string[] args ) { Task t = BadAsync(); t.Wait(); Console.WriteLine( "Task Status : {0}", t.Status ); Console.WriteLine( "Task IsFaulted: {0}", t.IsFaulted ); } static async Task BadAsync() { try { await Task.Run( () => { throw new Exception(); } ); } catch { Console.WriteLine( "Exception in BadAsync" ); } } }
5)在调用方法中同步的等待任务
调用方法可以调用任意多个异步方法并接收它们返回的Task对象,然后调用方法继续自己的操作,但在某个点可能需要等待某个task的完成,因此task类提供了Wait\WaitAll\WaitAny方法,堵塞调用方法,直到task完成。
class Program { static void Main() { DoRun(); } public static void DoRun() { Task<int> t1 = CountCharactersAsync( 1, "http://www.microsoft.com" ); Task<int> t2 = CountCharactersAsync( 2, "http://www.illustratedcsharp.com" ); Task<int>[] tasks = new Task<int>[] { t1, t2 }; //等到所有异步任务完成 Task.WaitAll( tasks ); //等到任一异步任务完成 //Task.WaitAny(tasks); Console.WriteLine( "Task 1: {0}Finished", t1.IsCompleted ? "" : "Not " ); Console.WriteLine( "Task 2: {0}Finished", t2.IsCompleted ? "" : "Not " ); //用到了t1.Result,会堵塞直到异步方法执行完成,等同于t1.Wait() Console.WriteLine("DoRun: Result = {0}", t1.Result); } private static async Task<int> CountCharactersAsync( int id, string site ) { WebClient wc = new WebClient(); string result = await wc.DownloadStringTaskAsync( new Uri( site ) ); return result.Length; } }
6)在异步方法中异步的等待任务
在异步方法中有多个异步task时,有时也需等待Task完成,可以用Task.WhenAll和Task.WhenAny方法。
class Program { static void Main() { DoRun(); } public static void DoRun() { Task<int> t = CountCharactersAsync("http://www.microsoft.com", "http://www.illustratedcsharp.com"); //用到了t.IsCompleted,不会堵塞。 Console.WriteLine("DoRun: Task {0}Finished", t.IsCompleted ? "" : "Not "); //用到了t.Result,会堵塞直到异步方法执行完成。等同于t.Wait() Console.WriteLine("DoRun: Result = {0}", t.Result); } private static async Task<int> CountCharactersAsync(string site1, string site2) { WebClient wc1 = new WebClient(); WebClient wc2 = new WebClient(); Task<string> t1 = wc1.DownloadStringTaskAsync(new Uri(site1)); Task<string> t2 = wc2.DownloadStringTaskAsync(new Uri(site2)); List<Task<string>> tasks = new List<Task<string>>(); tasks.Add(t1); tasks.Add(t2); //开始执行异步执行所有task,之前的代码时同步执行的。 await Task.WhenAll(tasks); Console.WriteLine(" CCA: T1 {0}Finished", t1.IsCompleted ? "" : "Not "); Console.WriteLine(" CCA: T2 {0}Finished", t2.IsCompleted ? "" : "Not "); return t1.IsCompleted ? t1.Result.Length : t2.Result.Length; } }
7)task类提供的其他方法
- Task.Delay
task.sleep会堵塞当前线程;但task.delay只是暂停对应的操作,不会堵塞线程,线程还可以做别的操作。 - Task.Yield()
在单线程异步情况下,当使用await task时,线程的使用权会返回给调用方法,让调用方法有机会用到线程。而task.Yield()会返回一个执行时间忽略不计的task(task对的方法直接返回,不进行任何操作),就可以完美交出线程使用权。
8)一个GUI方法实例
如下图所示,点击process按钮,程序将开始并持续更新进度条;成功完成时,弹出“Process completed normally”框;点击cancel按钮时,弹出“Process was cancelled at *”框。

UI代码如下:
<Window x:Class="WpfAwait.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Process and Cancel" Height="150 " Width="250"> <StackPanel> <Button Name="btnProcess" Width="100" Click="btnProcess_Click" HorizontalAlignment="Right" Margin="10,15,10,10">Process</Button> <Button Name="btnCancel" Width="100" Click="btnCancel_Click" HorizontalAlignment="Right" Margin="10,0">Cancel</Button> <ProgressBar Name="progressBar" Height="20" Width="200" Margin="10" HorizontalAlignment="Right"/> </StackPanel> </Window>
后台逻辑代码如下:
namespace WpfAwait { public partial class MainWindow : Window { CancellationTokenSource _cancellationTokenSource; public MainWindow() { InitializeComponent(); } private async void btnProcess_Click( object sender, RoutedEventArgs e ) { btnProcess.IsEnabled = false; _cancellationTokenSource = new CancellationTokenSource(); CancellationToken cancellationToken = _cancellationTokenSource.Token; int completedPercent = 0; for ( int i = 0; i < 10; i++ ) { if (cancellationToken.IsCancellationRequested ) break; try { await Task.Delay( 500, cancellationToken); completedPercent = ( i + 1 ) * 10; } catch ( TaskCanceledException ex ) { completedPercent = i * 10; } progressBar.Value = completedPercent; } string message = cancellationToken.IsCancellationRequested ? string.Format( "Process was cancelled at {0}%.", completedPercent ) : "Process completed normally."; MessageBox.Show( message, "Completion Status" ); progressBar.Value = 0; btnProcess.IsEnabled = true; btnCancel.IsEnabled = true; } private void btnCancel_Click( object sender, RoutedEventArgs e ) { if ( !btnProcess.IsEnabled ) { btnCancel.IsEnabled = false; _cancellationTokenSource.Cancel(); } } } }
7、并行计算(Parallel.For和Parallel.ForEach)
对于循环结构中,如果每一次迭代不依赖之前的迭代,而且程序运行在多核处理器上,将不同的迭代放在不同的处理器上运行,就可以实现并行计算。Parrallel.For和Parallel.ForEach是任务并行库(Task Parallel Library)中的两个循环方法,就是为此而生的。
1)Paraller.For示例:
namespace ExampleParallelFor { class Program { static void Main() { const int maxValues = 50; int[] squares = new int[maxValues]; Parallel.For( 0, maxValues, i => squares[i] = i * i ); for ( int i=0; i < maxValues; i++ ) Console.WriteLine( "Element {0}: {1}", i, squares[i] ); } } }
2)Parallel.ForEach示例
namespace ParallelForeach1 { class Program { static void Main() { string[] squares = new string[] { "We", "hold", "these", "truths", "to", "be", "self-evident", "that", "all", "men", "are", "created", "equal"}; Parallel.ForEach( squares, i => Console.WriteLine( string.Format( "{0} has {1} letters", i, i.Length ) ) ); } } }



浙公网安备 33010602011771号