C#异步Task介绍及在wpf中非UI线程调用UI界面控件

一、什么是异步和同步

异步和同步主要用于修饰方法,调用者需要等待方法执行完并返回后才能继续执行就称为同步方法,而一个方法被调用时立即返回,并获取一个线程执行该方法内部的业务,调用者不用等待该方法执行完毕,称为异步方法。

异步的好处在于非阻塞,可以把一些不需要立即使用结果,比较耗费时间的任务设置为异步执行,来提高程序的运行效率;另一方面,在UI界面中,为了能够使人机交互友好,也会把一些复杂耗费时间的任务设置为异步执行。

二、Task介绍

Task是在ThreadPool的基础上推出的。ThreadPool中有若干数量的线程,如果有任务需要处理时,会从线程池中获取一个空闲的线程来执行任务,任务执行完毕线程不会销毁,而是被线程池收回以供后续任务使用。当线程池中所有的线程都在忙碌时,又有新任务要处理时,线程池才会新建一个线程来处理该任务,如果线程数量达到设置的最大值,任务会排队,等待其他任务释放线程后再执行。线程池能减少线程的创建,节省开销。但ThreadPool的缺点是不能控制线程池中线程的执行顺序,也不能获取线程池内线程取消/异常/完成的通知。然而Task既拥有线程池的优点,同时也解决了使用线程池不易控制的弊端。

1.Task的创建和执行有如下三种:

//1.new方式实例化一个Task,需要通过Start方法启动
Task task=new Task(()=>{});
task.Start();
//2.Task.Factory.StaertNew(Action action)创建和启动一个Task
Task task2=Task.Factory.StartNew(()=>);
//3.Task.Run(Action action)将任务放在线程池队列,返回并启动一个Task
Task task3=Task.Run(()=>)

2.Task的wait/waitAll/waitAny方法

task.wait():表示要等待task执行完毕;task.waitall(new Task[] {task1,task2}):表示等待task1,task2都执行完毕;task.waitany(new Task[] {task1,task2}):表示等待task1,task2中任意一个任务执行完毕就停止阻塞。

 Task task1 = new Task(() =>
            {
                Thread.Sleep(500);
                Console.WriteLine("线程1执行完毕");
            });
            task1.Start();
            Task task2 = new Task(()=>
            {
                Thread.Sleep(1000);
                Console.WriteLine("线程2执行完毕!");
            });
            task2.Start();
            Task.WaitAny(new Task[] { task1, task2 });//task1,task2两个任务任意一个执行完毕,就解阻塞;
            Console.WriteLine("主线程执行完毕!");
            Console.ReadKey();

3.Task的WhenAll/WhenAny/ContinueWith

WhenAll(new Task[] {task1,task2}).ContinueWhit((t)=>{}):表示task1,task2都执行完毕后再去执行的操作;WhenAny(new Task[] {task1,task2}).ContinueWhit((t)=>{}):表示task1,task2中任意一个任务执行完毕后再去执行的操作;WhenAll/WhenAny不会阻塞主程序的执行。

            Task task1 = new Task(() =>
            {
                Thread.Sleep(500);
                Console.WriteLine("线程1执行完毕");
            });
            task1.Start();
            Task task2 = new Task(()=>
            {
                Thread.Sleep(1000);
                Console.WriteLine("线程2执行完毕!");
            });
            task2.Start();
            //task1,task2执行完了后执行后续操作
            Task.WhenAll(task1, task2).ContinueWith((t) =>
                {
                    Thread.Sleep(100);
                    Console.WriteLine("执行后续操作完毕!");
                });
            Console.WriteLine("主线程执行完毕!");
            Console.ReadKey();

4.Task执行取消任务(CancellationTokenSource)

  CancellationTokenSource source = new CancellationTokenSource();
            int index = 0;
            //任务被取消时注册的action会被执行
            source.Token.Register(() => {
                Console.WriteLine("任务被取消后执行XX操作!");
            });
            Task task1 = new Task(() => { 
                while(!source.IsCancellationRequested)
                {
                    Thread.Sleep(1000);
                    Console.WriteLine("第{0}次执行,线程运行中...",++index);
                }
            });
            task1.Start();
 //五秒后取消任务执行
            //Thread.Sleep(5000);
            //source.Cancel();
            source.CancelAfter(5000);//五秒后执行取消
            Console.ReadKey();

三、wpf中非UI线程调用UI界面控件

若要修改UI界面中的一个空间,可以使用如下方式:

//不带参数
this.richtxtChkRecorde.Dispatcher.Invoke(new Action(() => { SetDisplayInfo("原图"); }));
//带参数
str="原图";
this.richtxtChkRecorde.Dispatcher.Invoke(new Action((str) => { SetDisplayInfo(str); }));
//获取控件的值
string str="";
this.txtSingle.Dispathcher.Invoke(new Action()=>{str=txtSingle.Text;});
//设置控件的值
this.txtSingle.Dispathcher.Invoke(new Action()=>{txtSingle.Text=“你好”;});

winform中使用Control.Invoke()

出现错误:调用线程必须为 STA,因为许多 UI 组件都需要,可以用如下代码解决在非UI线程修改多个控件

 App.Current.Dispatcher.Invoke(new Action(() => { 
            Run r = new Run(string.Format("{0} {1}",
            DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), txt));
            if(txt.Contains("图像识别"))
            {
                r.Foreground = new SolidColorBrush(Colors.Red);
            }
            
            Paragraph para = new Paragraph();
            para.Inlines.Add(r);
            //para.Foreground =System.Windows.Media.Brush.;
            richtxtChkRecorde.Document.Blocks.Add(para); }));

参考:C#多线程和异步

发布了30 篇原创文章 · 获赞 1 · 访问量 1158

猜你喜欢

转载自blog.csdn.net/chunchunlaila/article/details/103262088