WPF异步编程总结

文章目录

  • 前言
  • 一、Thread
    • 1.不存在上下文同步的Thread
    • 2.存在上下文同步的Thread
    • 3.线程池ThreadPool
    • 4.粗粒度的 BackgroundWorker
  • 二、Task
    • 1.不存在上下文同步的Task
    • 2.Continuation
    • 3.ContinueWith
    • 4.TaskCompletionSource
    • 5.存在上下文同步的Task
    • 6.async&await
    • 7.异步中的进度报告
    • 8.task组合器
  • 三、异常捕获
  • 总结


前言

本文会学习到 ThreadTask线程池后台线程前台线程Continuation方法,关键字async,await、异步中的进度报告粗粒度细粒度Task组合器和异步过程中得异常捕获


一、Thread

1.不存在上下文同步的Thread

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;

解除阻塞条件:

  1. 阻塞条件被满足

  2. 操作超时(如果设置超时的话)

  3. 通过Thread.Interrupt()进行打断

  4. 通过Thread.Abord()进行终止

  5. 阻塞和解除阻塞会有上下文切换,

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...");
        }

2.存在上下文同步的Thread

//同步上下文 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);
        }
    }

3.线程池ThreadPool

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);
        }
    }

4.粗粒度的 BackgroundWorker

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

1.不存在上下文同步的Task

//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);
           
        }

2.Continuation

 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);
            });
            
        }

3.ContinueWith

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里的语句很简单的话,同步执行也是值得的
            */
        }

4.TaskCompletionSource

//创建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;
        }

5.存在上下文同步的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同步到上下文

6.async&await

/* 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();


            }
        }

7.异步中的进度报告

//异步中的进度报告
        //方法一:
        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);
                    }
                }
            });
        }

8.task组合器

//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);
        }
    }

总结

本文仅仅总结介绍了异步编程的使用,通过细粒度的异步方式避免使用锁,因此不介绍锁的使用。

你可能感兴趣的:(C#,wpf,c#,ui)