本文会学习到 Thread、Task,线程池,后台线程,前台线程,Continuation方法,关键字async,await、异步中的进度报告、粗粒度细粒度、Task组合器和异步过程中得异常捕获。
1.创建线程
public void Run()
{
//构造函数的入参是个委托
Thread t = new Thread(writeA);
t.Start();
for (int i = 0; i < 1000; i++)
{
Console.Write("C++");
}
Console.ReadLine();
}
void writeA()
{
Thread.CurrentThread.Name = "Sub Thread";
for (int i = 0; i < 1000; i++)
{
Console.Write("C#");
}
}
//运行结果:
//C++C++C++C++C++C++C++C++C++C++C++C++C#C#C#C#C#C#C#C#C#C#C#C#C#C#C#C++C++C++C++C++C++C++C++.....
//在单核计算机上,操作系统必须为每个线程分配时间片来模拟并发
//在多核计算机上,两个线程可以真正做到并行执行,出现这种情况,可能受其他进程活动竞争的影响,另外由于控制台处理并发请求机制的微妙性导致的。
说明:
线程一旦开始执行 IsAlive就是true,线程结束编程false;
线程结束的条件是:线程构造函数传入的委托结束了执行
线程一旦结束就无法重启
每个线程都有Name属性,通常用于调试,Name只能设置一次,以后更改会抛出异常
静态的Thread.CurrentThread属性会返回当前执行的线程
join和sleep :
join可以设置一个超时,用毫秒或者TimeSpan都可以,设置超时会返回bool类型,如果是true,线程结束了,如果超时了就返回false
join和sleep都是阻塞线程的方法
阻塞:
可以通过ThreadState这个属性来判断线程是否处于被阻塞状态:
bool blocked = (someThread.ThreadState & ThreadState.WaitSleepJoin) != 0;
解除阻塞条件:
阻塞条件被满足
操作超时(如果设置超时的话)
通过Thread.Interrupt()进行打断
通过Thread.Abord()进行终止
阻塞和解除阻塞会有上下文切换,
I/O-bound(阻塞) 和 CPU-bound(自旋)
I/O-bound:花费大部分时间等待某事发生。干等着:Sleep,join被视为I/O-bound
CPU-bound:花费大部分时间执行CPU运算
1.如果希望条件很快得到满足,短暂自旋会很有效,它避免了上下文切换的开销和延迟,.net framework提供了特殊的方法和类来提供帮助,SpinLock和SpinWait
以下是代码测试
包括线程名和线程异常测试、前台线程后台线程、提升线程的优先级、信号和阻塞时打断阻塞
/线程名和线程异常测试
public void Run1()
{
Thread.CurrentThread.Name = "Main Thread";
Thread t = new Thread(write1);
t.Name = "Sub Thread";
t.Start();
Console.WriteLine(Thread.CurrentThread.Name);
}
void write1()
{
//线程内出现异常,如果不捕获,可以订阅全局异常处理事件,查看App.xaml.cs,后面会贴出代码
try
{
Thread.CurrentThread.Name = "Sub1 Thread";
}
catch (Exception ex)
{
Console.WriteLine(ex.StackTrace);
}
Console.WriteLine(Thread.CurrentThread.Name);
}
//运行结果:
//Main Thread
//在 System.Threading.Thread.set_Name(String value)
//在 WPFTEST.CThread.write1() 位置 D:\WorkPro\C#\WPFTEST\WPFTEST\非上下文切换异步\Thread\CreateThread.cs:行号 50
//前台线程后台线程
public void Run2()
{
Thread t = new Thread(write2);
t.Name = "Sub Thread";
t.IsBackground=true;
t.Start();
}
void write2()
{
Thread.Sleep(3000);
Console.WriteLine(Thread.CurrentThread.Name);
}
//默认情况下,手动创建的线程就是前台线程,
//只要有前台线程运行,那应用程序一直处于活动状态
//但是后天线程却不行
//所有前台线程结束,应用程序就会停止
//任何后台线程也会突然终止
//可以通过IsBackground属性判断是否是前台现场还是后后台线程
//后台线程突然终止,finally块就不会执行
//如果让后天程序执行完,在退出,可以用join来等待线程
//提升线程的优先级
//就必须提升进程的优先级
public void Run3()
{
Process p = Process.GetCurrentProcess();
p.PriorityClass = ProcessPriorityClass.High;
}
//join可以设置一个超时,用毫秒或者TimeSpan都可以,设置超时会返回bool类型,如果是true,线程结束了,如果超时了就返回false
public void Run4()
{
//构造函数的入参是个委托
Thread t = new Thread(write4);
t.Start();
//等待,线程执行完,再往下执行,join可以设置一个超时,用毫秒或者TimeSpan都可以,设置超时会返回bool类型,
//如果是true,线程结束了,如果超时了就返回false
if (t.Join(2000))
{
for (int i = 0; i < 10; i++)
{
Console.WriteLine("B");
}
}
else
{
Console.WriteLine("TimeOut");
Console.WriteLine(t.IsAlive);
//关闭线程
//t.Abort();
//判断是否阻塞
if((t.ThreadState & System.Threading.ThreadState.WaitSleepJoin) != 0)
{
//打断阻塞,让线程运行,会抛出异常,同时清除中断信号,将中断标记位设置成 false,在catch块继续执行
t.Interrupt();
}
Console.WriteLine(t.IsAlive);
}
Console.ReadLine();
}
void write4()
{
try
{
Thread.Sleep(5000);
for (int i = 0; i < 10; i++)
{
//Thread.Sleep(0)这样调用,会导致线程立即放弃当前本身的时间片,自动将执行(Console.Write("A"))移交给其他线程,
//Thread.Yield()做同样的事情,但是它只会吧执行交给同一处理器上的其他线程
//当代码任何地方插入Thread.Yield()就导致程序被破坏,说明程序肯定有bug
Console.WriteLine("A");
}
}
catch
{
Console.WriteLine("Interrupt");
}
}
//信号
ManualResetEvent signal = new ManualResetEvent(false);
public void Run5()
{
Thread t = new Thread(write5);
t.IsBackground = true;
t.Start();
Thread.Sleep(3000);
//打开信号
signal.Set();
//关闭信号,线程继续等待
signal.Reset();
Thread.Sleep(3000);
signal.Set();
}
void write5()
{
Console.WriteLine("wating...");
signal.WaitOne();
//signal.Dispose();
Console.WriteLine("stop...");
Thread.Sleep(1000);
Console.WriteLine("wating...");
signal.WaitOne();
//signal.Dispose();
Console.WriteLine("stop...");
}
//同步上下文 Dispatcher 方法
// Dispatcher的作用是用于管理线程工作项队列
class UIThread : UserControl
{
//用2个控件体现
public Button button = null;
public TextBlock txt = null;
public UIThread()
{
button = new Button();
button.Height = 30;
button.Width = 100;
txt = new TextBlock();
txt.Height = 50;
txt.Width = 100;
button.Click += new RoutedEventHandler(btn_Click);//鼠标左键
}
private void btn_Click(object sender, RoutedEventArgs e)
{
new Thread(work).Start();
}
void work()
{
Thread.Sleep(3000);
updateMes();
}
void updateMes()
{
//所有的控件对象都有Dispatcher对象,该对象接受一个委托,
//BeginInvoke通过将委托排队到UI线程的消息队列来执行
//Invoke执行相同的操作,但会阻塞当前线程,直到ui线程处理完,因此invoke允许获取返回值
Action act = () => { txt.Text = "clicked"; };
Dispatcher.BeginInvoke(act);
}
}
//同步上下文 SynchronizationContext 方法
class UIThread1
{
public Button button = null;
public TextBlock txt = null;
//同步上下文,可以向该类附加的ui线程发送数据
SynchronizationContext _sync = null;
public UIThread1()
{
button = new Button();
button.Height = 30;
button.Width = 100;
txt = new TextBlock();
txt.Height = 50;
txt.Width = 100;
button.Click += new RoutedEventHandler(btn_Click);//鼠标左键
_sync = SynchronizationContext.Current;
}
private void btn_Click(object sender, RoutedEventArgs e)
{
new Thread(work).Start();
}
void work()
{
Thread.Sleep(3000);
updateMes();
}
void updateMes()
{
_sync.Post(_ => txt.Text = "clicked", null);
}
}
class CThreadPool
{
public CThreadPool()
{
ThreadPool.SetMinThreads(10, 10);
}
private void doWork(object count)
{
Console.WriteLine("Putout: {0}", (int)count);
}
public void AddTask(int info)
{
ThreadPool.QueueUserWorkItem(new System.Threading.WaitCallback(doWork), info);
}
}
class BackGroundThread
{
//这种是粗粒度的异步,容易出现线程安全问题
//整个同步调用都发生在work上
private BackgroundWorker _threadWorker; //后台处理工作线程;
public BackGroundThread()
{
_threadWorker = new BackgroundWorker();
_threadWorker.DoWork += new DoWorkEventHandler(this.bkthreadWork_DoWork);
_threadWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(this.bkthreadWork_WorkCompleted);
}
public void Start()
{
_threadWorker.RunWorkerAsync(20);
Console.WriteLine($"Start Running on thread {Thread.CurrentThread.ManagedThreadId}");
}
private void bkthreadWork_DoWork(object sender, DoWorkEventArgs e)
{
//如果异步期间需要上下文同步,就要使用上下文同步的方法,Dispatcher或者SynchronizationContext
Console.WriteLine($"DoWork Running on thread {Thread.CurrentThread.ManagedThreadId}");
for (int i = 0; i < (int)e.Argument; i++)
{
Thread.Sleep(1000);
Console.Write("A");
}
e.Result = e.Argument;
}
private void bkthreadWork_WorkCompleted(object sender, RunWorkerCompletedEventArgs e)
{
Console.WriteLine("PutOut Count {0}", (int)e.Result);
Console.WriteLine($"WorkCompleted Running on thread {Thread.CurrentThread.ManagedThreadId}");
//这里又回到了调用线程
}
}
//Task默认使用线程池,也就是后台线程
//这种方法非常适合短时间运行的计算任务
public void Run()
{
Task task = Task.Run(() =>
{
Thread.Sleep(30000);
Console.WriteLine("task");
});
Console.WriteLine(task.IsCompleted);
Console.WriteLine(task.Status);
//Wait方法会阻塞知道task运行完成,相当于thread的join方法,
//可以指定超时时间,和一个取消令牌提前结束等待
task.Wait();
Console.WriteLine(task.IsCompleted);
Console.WriteLine(task.Status);
}
//长时间运行的任务或者阻塞任务不采用线程池,使用LongRunning
//如果同时运行多个LongRunning,性能会大受影响,解决办法是
//1.如果是IO-bound,TaskCompletionSource和异步函数可以让你用回调Continuation代替线程实现并发,
//2.如果是CPU-bound,使用生产者消费者队列,对并发进行限流
public void Run1()
{
Task task = Task.Factory.StartNew(() =>
{
Thread.Sleep(30000);
Console.WriteLine("task");
},TaskCreationOptions.LongRunning);
Console.WriteLine(task.IsCompleted);
Console.WriteLine(task.Status);
//Wait方法会阻塞知道task运行完成,相当于thread的join方法,
//可以指定超时时间,和一个取消令牌提前结束等待
task.Wait();
Console.WriteLine(task.IsCompleted);
Console.WriteLine(task.Status);
}
/* TASK的返回值 Task
* 使用委托或兼容的lambda表达式就可以得到Task
* 可以通过Result属性获得返回结果
* 如果Task还没完成操作,访问Result属性会阻塞线程,直到Task完成操作
*/
public void Run2()
{
Task<int> task = Task.Run(() =>
{
Thread.Sleep(3000);
Console.WriteLine("task");
return 3;
});
Console.WriteLine(task.IsCompleted);
int result = task.Result;
Console.WriteLine(task.IsCompleted);
Console.WriteLine(result);
}
/* TASK的异常:被抛出,捕获
* 如果Task抛出了一个未处理的异常,那么该异常会重新抛出给调用wait的地方,或者Result属性的地方
*/
public void Run3()
{
Task task = Task.Run(() =>
{
List<string> m = new List<string>();
m[1] = "";
});
try
{
task.Wait();
}
catch (AggregateException aex)
{
Console.WriteLine(aex.InnerException.StackTrace);
}
}
/* TASK的异常:没有抛出
* 如果Task已经处理的异常,可以通过IsFaulted,IsCanceled属性判断
* 如果两个属性都是false,那么就没用错误发生
*/
public void Run4()
{
Task task = Task.Run(() =>
{
throw null;
});
//这里不能调用wait,调用wait就会把异常抛出,就会被App中订阅的的全局异常捕获到
//task.Wait();
//要延时,Task属于后台线程,前台线程结束,就会停止
Thread.Sleep(3000);
if (task.IsFaulted == true)
{
Console.WriteLine(task.Exception.Message);
}
if (task.IsCanceled == true)
{
//说明有一个OperationCanceledException被抛出了
Console.WriteLine("OperationCanceledException throw");
}
}
/* TASK的异常:没有观察到
* 前提订阅了TaskScheduler.UnobservedTaskException,且Exception没有被访问, 那么异常会被捕获,如果Exception被访问了,就是观察到了,就不会捕获
*/
public void Run5()
{
Task task = Task.Run(() =>
{
throw null;
});
//要延时,Task属于后台线程,前台线程结束,就会停止
Thread.Sleep(3000);
}
public void Run()
{
//一个耗时操作
Task<int> task = Task.Run(() =>
Enumerable.Range(2, 3000000).Count(n =>
Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0)));
//awaiter特点:只有存在这些属性,就是awaiter
//1.存在属性OnCompleted
//2.存在属性GetResult
//3.存在属性IsCompeleted
//如果同步上下文出现了,OnCompleted会自动捕获它,并将Continuation提交到这个上下文中,例如会把Continuation放回到UI线程中,可以用ConfigureAwait(false)避免这种行为
//如果没有上下文同步或者使用了ConfigureAwait(false),Continuation会运行在先去task的同一个线程上,从而避免不必要的开销。
var awaiter = task.ConfigureAwait(false).GetAwaiter();
awaiter.OnCompleted(() =>
{
//如果task发生异常,这里会抛出
int result = awaiter.GetResult();
Console.WriteLine(result);
});
}
Task.ContinueWith
//和task组合使用会有意想不到的效果,例如taskA依赖taskB执行
public void Run2()
{
//一个耗时操作
Task<int> task = Task.Run(() =>
Enumerable.Range(2, 3000000).Count(n =>
Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0)));
//ContinueWith本身返回一个task,它可以附加更多类型的Continuation
task.ContinueWith(_task=>
{
int result = _task.Result;
Console.WriteLine(result);
});
}
//Task.ContinueWith:异常情况
public void Run3()
{
Console.WriteLine($"*******Main Running on thread {Thread.CurrentThread.ManagedThreadId}");
for (int i = 0; i < 30; i++)
{
Task.Run(async () =>
{
Console.WriteLine($"Running on thread {Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(2000);
});
}
Task t = Task.Run(async () =>
{
Console.WriteLine($"=======t1 Running on thread {Thread.CurrentThread.ManagedThreadId}");
await Task.Delay(2000);
Console.WriteLine($"=======t2 Running on thread {Thread.CurrentThread.ManagedThreadId}");
});
//Thread.Sleep(5000);
t.ContinueWith(_ =>
{
Console.WriteLine($"*******CW Running on thread {Thread.CurrentThread.ManagedThreadId}");
}, TaskContinuationOptions.ExecuteSynchronously);//, TaskContinuationOptions.ExecuteSynchronously
/*
* 这段代码首先创建了30个干扰Task,这样能显著降低即使不用ExecuteSynchronously,
* 线程池也会分配原线程来执行Continue任务的概率。运行后发现,任务t和continue确实是在同一个线程上执行的。
* 而注释掉TaskContinuationOptions.ExecuteSynchronously后,continue就会由线程池重新分配线程。
* 而如果取消注释线程Sleep 5秒这行代码,即使ExecuteSynchronously,continue也会由线程池重新分配线程执行,
* 这正如上一段文档中提到的:调用ContinueWith时,如果原任务已经执行完毕,
* 那么会由调用ContinueWith的线程执行continue任务,在这里就会由主线程来执行continue任务
*/
/*
* ExecuteSynchronously为什么不是默认行为
* 微软工程师Stephen Toub在其一篇博文中解释了为什么.NET团队没有把ExecuteSynchronously作为默认方案。
* 一个Task任务有可能会多次调用ContinueWith方法,如果默认是在同一线程执行,那么所有的continue任务都需要等待上一个continue完成后才能执行,这也就失去了并行的意义。
* 还有一种常见的情况就是很多个continue任务一个接一个的串在一起,如果这些continue任务都是同步顺序执行的,一个任务完成了就会执行下一个任务,
* 这将导致线程栈上堆积的frame越来越多,这有可能会导致线程栈溢出。为了解决溢出的问题,通常的解决方式是借用一个“蹦床”,
* 把需要完成的工作在当前线程栈之外保存起来,然后利用一个更高level的frame检索存储的任务并执行。这样一来,每次完成一个任务之后,并不是立即执行下一个任务,
* 而是将其保存至上述的frame并退出,该frame将执行下一个任务。而TPL正是利用这一方式来提升异步的执行效率。
* 以上就是没有默认同步运行任务的主要原因,虽然性能上会稍有损失,但这样可以更好的利用并行,更安全,而这性能的损失通常来说并不是最重要的。
* 作者最后也建议我们如果Task里的语句很简单的话,同步执行也是值得的
*/
}
//创建task的两种方式
//1.Task.Run
//2.TaskCompletionSource:让你在稍后开始和结束的任意操作中创建task
//3.和task组合使用会有意想不到的效果,例如taskA依赖taskB执行
//初始化一个实例即可
//它有一个Task属性可返回
//该Task完全由TaskCompletionSource对象控制
//调用任意一个方法都会给Task发信号:完成,完成取消
//这些方法只能调用一次
public void Run4()
{
//Delay(5000).ContinueWith(_task => { int result = _task.Result; Console.WriteLine(result); });
Delay(5000).GetAwaiter().OnCompleted(()=> { Console.WriteLine(5000); });
Console.WriteLine(1111);
}
Task<int> Delay(int ms)
{
var tcs = new TaskCompletionSource<int>();
var timer = new System.Timers.Timer(ms) { AutoReset = false };
timer.Elapsed += delegate { timer.Dispose(); tcs.SetResult(ms); };
timer.Start();
return tcs.Task;
}
public Button button = null;
public TextBlock txt = null;
public CContinuation()
{
button = new Button();
button.Height = 30;
button.Width = 100;
txt = new TextBlock();
txt.Height = 50;
txt.Width = 100;
//button.Click += new RoutedEventHandler(btn_Click);//鼠标左键
}
public void Run()
{
Task<int> task = Task.Run(() =>
Enumerable.Range(2, 3000000).Count(n =>
Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0)));
//awaiter特点:只有存在这些属性,就是awaiter
//1.存在属性OnCompleted
//2.存在属性GetResult
//3.存在属性IsCompeleted
var awaiter = task.GetAwaiter();
//如果同步上下文出现了,OnCompleted会自动捕获它,并将Continuation提交到这个上下文中,例如会把Continuation放回到UI线程中
awaiter.OnCompleted(() =>
{
int result = awaiter.GetResult();
Console.WriteLine(result);
txt.Text += awaiter.GetResult().ToString();
});
}
//Task.ContinueWith
public void Run2()
{
//一个耗时操作
Task<int> task = Task.Run(()=>
{
Console.WriteLine($"*******11111 Running on thread {Thread.CurrentThread.ManagedThreadId}");
return Enumerable.Range(2, 3000000).Count(n =>
Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0));
}
);
//ContinueWith本身返回一个task,它可以附加更多类型的Continuation
//必须直接处理AggregateException:
//如果task发生异常需要写额外的代码把Continuation封装到UI应用上
var mt = task.ContinueWith(_task =>
{
Thread.Sleep(2000);
Console.WriteLine($"*******2222 Running on thread {Thread.CurrentThread.ManagedThreadId}");
});
mt.GetAwaiter().OnCompleted(() => { txt.Text += "111"; });
}
//异步编程注意
//命令式的循环结构不要和continuation混合在一起,因为他们依赖当前本地状态,
//避免在上层函数进行异步,会增加不必要的异步锁和线程安全问题,尽量做到依赖底层函数异步,在通过continuation同步到上下文
/* await:
* await关键字附加了continuation的过程
* var res = await task一类,不单独指Task
* statement(s)
*
* 等价于
*
* var awaiter = task.GetAwaiter();
* awaiter.OnCompleted(()=>{
* var res = awaiter.GetResult();
* statement(s);
* });
* await可以捕获本地状态,可以出现在任何地方,在异步方法内可以替换任何表达式除了lock表达式和unsafe上下文
* await表达式之后编译器依赖continuation来执行
* await的表达式通常是一个 task,也可以是满足下列条件的任意对象
* 1.有GetAwaiter的方法它返回一个awater
* 2.返回适当类型的GetReault方法
* 3.一个bool类型的IsCompleted属性
*
* async:
* async修饰符会让编译器把await当做关键字而不是标识符
* asyn修饰符只能用于方法包括lambda,方法可以返回void Task Task<>
* async修饰符对方法的签名或public元数据没有影响,只会影响方法内部
* 在接口内使用async没有意义
* 使用async来重载非async的方法是合法的
* 使用了async修饰符的方法就是“异步函数”,遇到await表达式执行
* 执行完会跳到原方法从停止的地方继续执行,如果发生故障那么异常会被从新抛出,
*/
/* ui上的await:下面的例子
* 例子中只有GetPrimesCountAsync在线程上运行
* 但是DisplayPrimeCountAsync中的for循环部分代码,会租用UI线程上的时间,也就是说,循环体和UI线程处理其他事件是穿插执行的
* 因为唯一抢占的时刻就是在await期间
* 真正的并发代码要避免共享状态和UI控件
*
* 如果不需要弹回到UI线程,就是说,txt.Text不是ui控件,可以使用Task.ConfigureAwait(false)
*/
async public void Run3()
{
await DisplayPrimeCountAsync();
}
async Task DisplayPrimeCountAsync()
{
for(int i=0;i<10;i++)
{
txt.Text += await GetPrimesCountAsync()+Environment.NewLine;
}
}
Task<int> GetPrimesCountAsync()
{
return Task.Run(() =>
{
return Enumerable.Range(2, 3000000).Count(n =>
Enumerable.Range(2, (int)Math.Sqrt(n) - 1).All(i => n % i > 0));
} );
}
//自己封装异步操作
//Thread.Sleep()是同步延迟,Task.Delay()是异步延迟。
// Thread.Sleep()会阻塞线程,Task.Delay()不会。
//Thread.Sleep()不能取消,Task.Delay()可以。
//Task.Delay()实质创建一个运行给定时间的任务,Thread.Sleep()使当前线程休眠给定时间。
//反编译Task.Delay(),基本上讲它就是个包裹在任务中的定时器。
//Task.Delay()和Thread.Sleep()最大的区别是Task.Delay()旨在异步运行,在同步代码中使用Task.Delay()是没有意义的;在异步代码中使用Thread.Sleep()是一个非常糟糕的主意。通常使用await关键字调用Task.Delay()。
//我的理解:Task.Delay(),async/await和CancellationTokenSource组合起来使用可以实现可控制的异步延迟
async public void Run4()
{
var canceSource = new CancellationTokenSource(5000);
try {
await Foo(canceSource.Token);
}
catch(OperationCanceledException e)
{
Console.WriteLine(e.StackTrace);
}
}
async Task Foo(CancellationToken canceToken)
{
for (int i = 0;i< 100;i++)
{
Console.WriteLine(i);
//会立即停止
await Task.Delay(1000, canceToken);
canceToken.ThrowIfCancellationRequested();
}
}
//异步中的进度报告
//方法一:
async public void Run5()
{
Action<int> proChange = i => Console.WriteLine(i + "%");
await Foo(proChange);
}
Task Foo(Action<int> proChange)
{
return Task.Run(() =>
{
for (int i = 0; i < 1000; i++)
{
if (i % 10 == 0)
{
proChange(i / 10);
}
}
});
}
//方法二:
async public void Run6()
{
var proChange =new Progress<int> (i => Console.WriteLine(i + "%"));
await Foo(proChange);
}
Task Foo(IProgress<int> proChange)
{
return Task.Run(() =>
{
for (int i = 0; i < 1000; i++)
{
if (i % 10 == 0)
{
proChange.Report(i / 10);
}
}
});
}
//task组合器
//Task.WenAny:适合为不支持超时或取消的操作,例如 Task wa = await Task.WhenAny(Delay1(), Delay2(),Delay3(),Task.Delay(2000));
//Task.All,Task wa = await Task.WhenAny(Delay1(), Delay2(),Delay3());所有的Task完成后返回一个Task,包含的结构是数组[],
async public void Run7()
{
//返回最先完成的Task
Task<int> wa = await Task.WhenAny(Delay1(), Delay2(),Delay3());
Console.WriteLine(wa.Result);
Console.WriteLine("0000");
}
async Task<int> Delay1() { await Task.Delay(3000);return 1; }
async Task<int> Delay2() { await Task.Delay(4000); return 2; }
async Task<int> Delay3() { await Task.Delay(5000); return 3; }
///
/// App.xaml 的交互逻辑
///
public partial class App : Application
{
public App()
{
//通过消息循环调用的程序任何部分发生未处理的异常后,将触发这些异常,但是非UI线程上的未处理异常,并不会触发
Application.Current.DispatcherUnhandledException += App_DispatcherUnhandledException;
TaskScheduler.UnobservedTaskException += App_UnobservedTaskException;
//任何线程有任何未处理的异常都会触发
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
}
private void App_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
{
MessageBox.Show(e.Exception.StackTrace);
//throw new NotImplementedException();
}
private void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
//try
//{
// Exception ex = e.ExceptionObject as Exception;
// MessageBox.Show(ex.StackTrace);
//}
//catch { }
//throw new NotImplementedException();
Exception ex = e.ExceptionObject as Exception;
MessageBox.Show(ex.StackTrace);
}
private void App_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
MessageBox.Show(e.Exception.Source);
}
}
本文仅仅总结介绍了异步编程的使用,通过细粒度的异步方式避免使用锁,因此不介绍锁的使用。