20181105_线程之Task

Task是基于.net Framework3.0框架, Task使用的线程也是来自于ThreadPool

多线程的两个意义: 优化体验(常见于不卡界面), 提升运行速度(不同线程可以分担运算任务)

总结:

//Task6个方法:
WaitAll    Task.WaitAny(taskList.ToArray());//会阻塞当前线程,等着某个任务完成后,才进入下一行  卡界面; 有好几个重载, 超时. . .
WaitAny    //Task.WaitAny(taskList.ToArray());//会阻塞当前线程,等着[某个任务]完成后(只要有一个完成, 就会进入下一行代码),才进入下一行  卡界面

应用场景
//一个业务查询操作有多个数据源  首页--多线程并发--拿到全部数据后才能返回  WaitAll
//一个商品搜素操作有多个数据源,商品搜索--多个数据源--多线程并发--只需要一个结果即可-- 

WhenAll     这个WhenAll啥也没有做, 就是表示当所有线程都把事情做完之后,然后再去做什么; 他必须要和ContinuWith一起使用, 否则便失去意义; Task.WhenAll(taskList).ContinueWith(()={"一起去吃饭"});翻译一下就是, 当tasklist中所有线程都完成工作了, 那么继续执行  一起去吃饭
WhenAny     这个和WhenAll一样. 但是指的是任意一个线程完成

ContinueWith    ContinueWhenAll

taskFactory.ContinueWhenAll   在taskFactory中也有类似的WhenAll 和 WhenAny

//Task.Delay(1000);//延迟  不会卡
//Thread.Sleep(1000);//等待   卡

一.   Task.Run→线程的启动是基于异步形式, 下面代码演示使用Task/new Task() / TaskFactory方式来启动线程:

  Task.Run(() => this.DoSomethingLong("btnTask_Click1"));//Task的Run在.net 4.5 的时候才有
            TaskFactory taskFactory = Task.Factory;//从.net 4.0开始才有
            taskFactory.StartNew(() => this.DoSomethingLong("btnTask_Click3")); //利用TaskFactroy来启动一个新的线程 

             Task t = new Task(() => { Console.WriteLine("opasdfawerfwe"); }); 

             t.Start();

 二. 使用task实现等待当前线程完成, WaitAll和WaitAny, 然后继续后面的任务(卡界面):

////什么时候用多线程? 任务能并发运行;提升速度;优化体验
                List<Task> taskList = new List<Task>();
                Console.WriteLine($"项目经理启动一个项目。。【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
                taskList.Add(Task.Run(() => this.Coding("悟空", "Client")));//做客户端
                taskList.Add(Task.Run(() => this.Coding("八戒", "Portal")));//做门户(后台)
                taskList.Add(Task.Run(() => this.Coding("唐僧", "Service")));//做服务
                taskList.Add(Task.Run(() => this.Coding("如来", "Jump"))); //做项目跳转
                taskList.Add(Task.Run(() => this.Coding("沙僧", "Monitor")));//做监控系统
////阻塞:需要完成后再继续, 谁执行下面的Task, 就会阻塞谁
Task.WaitAll(taskList.ToArray());//会阻塞当前线程,等着 [全部任务] 完成后,才进入下一行  卡界面

                Task.WaitAny(taskList.ToArray());//会阻塞当前线程,等着 [某个任务] 完成后(只要有一个完成, 就会进入下一行代码),才进入下一行  卡界面,多线程加快速度,但是全部任务完成后,才能执行的操作
                Task.WaitAny(taskList.ToArray(), 1000); //限时等待 

                Console.WriteLine($"告诉甲方验收,上线使用【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");   

小结:

 waitAll→一个业务查询操作有多个数据源  首页--多线程并发--拿到全部数据后才能返回  WaitAll
                WaitAny→一个商品搜素操作有多个数据源,商品搜索--多个数据源--多线程并发--只需要一个结果即可--WaitAny

三. 使用WaitAll或WaitAny即等待任务完成, 又不卡UI的方法, 包一层

Task.Run( //再包一层Task, 就是避开当前的UI线程来执行运算方法
                    () =>
                    {
                        //多线程加快速度,但是全部任务完成后,才能执行的操作
                        Task.WaitAll(taskList.ToArray());//会阻塞当前线程,等着全部任务完成后,才进入下一行  卡界面
                        Console.WriteLine($"告诉甲方验收,上线使用【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");

四. 使用WhenAll 和 WhenAny, 来实现即不卡界面, 又可以控制执行顺序(等待所有任务完成, 才执行后续任务)

//当任何一个(WhenAny)线程完成任务, 就继续执行(ContinueWith)后面的动作
                Task.WhenAny(taskList.ToArray()).ContinueWith(t =>
                {
                    Console.WriteLine(taskList.ToArray().FirstOrDefault(s => s.Status == TaskStatus.RanToCompletion));
                    Console.WriteLine($"风骚的笑. . .【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
                }) ;
                //当所有(WhenAll)线程完成任务, 就继续执行(ContinueWith)后面的动作
                Task.WhenAll(taskList.ToArray()).ContinueWith(t =>
                {
                    Console.WriteLine($"部署环境,联调测试。。。【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
                }) ; 

五.   实战:

a)         Task控制线程数量控制, 完成1000个任务, 但是最多只能启动11个线程, 代码如下:  

//Task控制线程数量
                List<int> list = new List<int>();
                for (int i = 0; i < 10000; i++)
                {
                    list.Add(i);
                }
                //项目规定完成10000个任务  但是最多只能有11个线程
                Action<int> action = i =>
                {
                    Console.WriteLine(Thread.CurrentThread.ManagedThreadId.ToString("00"));
                    Console.WriteLine($"当前i的值是→{ i.ToString("00")}");
                    Thread.Sleep(new Random(i).Next(100, 300));
                };
                List<Task> taskList = new List<Task>();

                //将任务从list中取出来
                foreach (var i in list)
                {
                    int k = i;
                    taskList.Add(Task.Run(() => action.Invoke(k))); //每创建一个任务, 就将任务添加到itasklist中; 这里是异步执行的
                    if (taskList.Count > 10)//检查是否大于10, 就开始等待; 注意如果这样判断的话, 其实当前用了11个线程
                    {
                        //WaitAny  也是卡界面
                        Task.WaitAny(taskList.ToArray());//最起码要完成一个
                        //这一步检查线程的状态, 如果线程的状态不等于 已完成  的, 则继续保留在taskList中
                        taskList = taskList.Where(t => t.Status != TaskStatus.RanToCompletion).ToList();
                        //var v = taskList.Max(m => m.Id);
                        //Console.Write(v + "....................
.................");
                    }
                }
                //等待所有线程完成后, 开始后续的任务
                Task.WhenAll(taskList.ToArray()); 

b)         使用TaskFactory的AsyncState来明确哪一个线程最先完成任务  

 //不能使用Task启动,使用TaskFactory是可以的, 可以使用t.AsyncState状态来标识
               TaskFactory taskFactory1 = new TaskFactory();
                List<Task> taskList = new List<Task>();
                taskList.Add(taskFactory1.StartNew(o => this.Coding("悟空", "Client"), "悟空"));
                taskList.Add(taskFactory1.StartNew(o => this.Coding("八戒", "Portal"), "八戒"));
                taskList.Add(taskFactory1.StartNew(o => this.Coding("沙僧", "Service"), "沙僧"));
                //只要有任何一个人完成任务之后, 就获取整个人的编号
                taskFactory1.ContinueWhenAny(taskList.ToArray(), t =>
                {
                    Console.WriteLine(t.AsyncState);
                    Console.WriteLine($"部署环境,联调测试。。。【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
                });
                //当所有的人都完成任务之后, 获取第一个完成任务的人
                taskFactory1.ContinueWhenAll(taskList.ToArray(), tList =>
                {
                    Console.WriteLine(tList[0].AsyncState);
                    Console.WriteLine($"部署环境,联调测试。。。【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");
                }); 

c)         Delay和Sleep  

{
                    //Task.Delay(1000);//延迟  不会卡 ; 延迟1秒钟后, 执行后续动作
                    //Thread.Sleep(1000);//等待   卡

                    Stopwatch stopwatch = new Stopwatch();
                    stopwatch.Start();
                    Thread.Sleep(2000);
                    stopwatch.Stop();
                    Console.WriteLine(stopwatch.ElapsedMilliseconds);
   }

            {
                Stopwatch stopwatch = new Stopwatch();
                stopwatch.Start();
                //不卡界面; 然后会等待2000毫秒后去执行continueWith中的函数;
                Task.Delay(2000).ContinueWith(t =>
                {
                    stopwatch.Stop();
                    Console.WriteLine(stopwatch.ElapsedMilliseconds);
                });
            }

            {
                //使用Task.Run将Thread.Sleep包一层 同样可以实现不卡界面, 延迟执行
                //Stopwatch stopwatch = new Stopwatch();
                //stopwatch.Start();
                //Task.Run(() =>
                //{
                //    Thread.Sleep(2000);
                //    stopwatch.Stop();
                //    Console.WriteLine(stopwatch.ElapsedMilliseconds);
                //});
            } 

d)         Task指定回调 ; ContinueWith 

Task.Run(() => this.Coding("悟空", "Client")).ContinueWith(t => { }); 

六. 使用Parallel并行编程, 在Task的基础上做了封装, 从.net Framework4.0开始

a)   使用Parallel并行编程完成一个多线程启动; 注意这个会卡界面; 因为主线程参与了计算

//当然这个也可以使用Task来完成
Parallel.Invoke(() => this.Coding("悟空", "Client")
                    , () => this.Coding("八戒", "Portal")
                    , () => this.Coding("沙僧", "Service"));

 b) 一些简单API; 使用for和foreach 

//for循环的操作; 启动5个线程; 0表示for的开始, 5表示结束; 简化的for
                Parallel.For(0, 5, i => this.Coding("悟空", "Client" + i));
				//使用foreach; 对集合中的每一个元素都执行后面的Action
			  Parallel.ForEach(new string[] { "0", "1", "2", "3", "4" }, i => this.Coding("悟空", "Client" + i));

  c)  使用 parallel来控制当前最大的线程数量; 卡界面

//parallelOptions 可以控制并发数量;控制线程数量;
                ParallelOptions parallelOptions = new ParallelOptions();
                parallelOptions.MaxDegreeOfParallelism = 3; //任意时刻只有三个线程在执行
                Parallel.For(0, 10, parallelOptions, i => this.Coding("悟空", "Client" + i));

  d) 使用 parallel来控制当前最大的线程数量; 不卡界面

Task.Run(() =>
                {
                    ParallelOptions parallelOptions = new ParallelOptions();
                    parallelOptions.MaxDegreeOfParallelism = 3;
                    Parallel.For(0, 10, parallelOptions, i => this.Coding("悟空", "Client" + i));                });

  e)   Parallel的break和stop, 不推荐使用

//Break  Stop  都不推荐用
ParallelOptions parallelOptions = new ParallelOptions();
parallelOptions.MaxDegreeOfParallelism = 3;
Parallel.For(0, 40, parallelOptions, (i, state) =>
{
   if (i == 2)
      {
         Console.WriteLine($"线程Break,当前任务结束 i={i}  {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
         state.Break();//结束Parallel当次的这一次操作  结束了当前的线程的操作, 如果当前线程操作了很多i , 那么这个线程所操作的i就不会输出了; 但是如果当前线程如果是主线程的话, 那么它会直接结束Parallel的, 这要看运气了.  . .
         return;//必须带上
      }
   //if (i == 20)
   //{
   //    Console.WriteLine($"线程Stop,Parallel结束 i={i} {Thread.CurrentThread.ManagedThreadId.ToString("00")}");
   //    state.Stop();//结束Parallel全部操作   等于break
   //    return;//必须带上
   //}
   this.Coding("悟空", "Client" + i);
   });
   //Break 实际上结束了当前这个线程;如果是主线程,等于Parallel都结束了
   //多线程的终止本身就不靠谱

Task.Run( //再包一层Task, 就是避开当前的UI线程来执行运算方法

() =>

{

//多线程加快速度,但是全部任务完成后,才能执行的操作

Task.WaitAll(taskList.ToArray());//会阻塞当前线程,等着全部任务完成后,才进入下一行  卡界面

Console.WriteLine($"告诉甲方验收,上线使用【{Thread.CurrentThread.ManagedThreadId.ToString("00")}】");

原文地址:https://www.cnblogs.com/wxylog/p/9911524.html

时间: 2024-07-31 07:48:39

20181105_线程之Task的相关文章

Asp.Net Core 轻松学-多线程之Task(补充)

前言 ????在上一章 Asp.Net Core 轻松学-多线程之Task快速上手 文章中,介绍了使用Task的各种常用场景,但是感觉有部分内容还没有完善,在这里补充一下. 1. 任务的等待 在使用 Task 进行基于队列的异步任务(TAP)的时候,对于刚入门的同学来说,只是简单的了解了使用 Task 可以在后台处理异步任务,但是对于阻塞调用可能还有有一些不太明白,异步任务默认是不阻塞的执行过程,当一个 Task 被创建出来的时候,并没有被压入队列中,而是开始执行的时候,才会进入队列中:执行一个

C# 多线程之Task资料

博客 Stephen Toub From MicroSoft Crop. Stephen Cleary Parallelism in .NET   文章 It's All About the SynchronizationContext How would I run an async Task<T> method synchronously? Should I expose synchronous wrappers for asynchronous methods?

java多线程之Future和FutureTask

Executor框架使用Runnable 作为其基本的任务表示形式.Runnable是一种有局限性的抽象,然后可以写入日志,或者共享的数据结构,但是他不能返回一个值. 许多任务实际上都是存在延迟计算的:执行数据库查询,从网络上获取资源,或者某个复杂耗时的计算.对于这种任务,Callable是一个更好的抽象,他能返回一个值,并可能抛出一个异常.Future表示一个任务的周期,并提供了相应的方法来判断是否已经完成或者取消,以及获取任务的结果和取消任务. public interface Callab

Java多线程之Wait()和Notify()

1.Wait()和Notify.NotifyAll都是Object的方法 2.多线程的协作是通过控制同一个对象的Wait()和Notify()完成 3.当调用Wait()方法时,当前线程进入阻塞状态,直到有另一线程调用了该对象的Notify()方法 package Thread.Wait; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.con

Java多线程之notifyAll的作用域

notifyAll()因某个特定锁而被调用时,只有等待这个锁的任务才会被唤醒. package Thread.Wait; import java.util.Timer; import java.util.TimerTask; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; class Blocker { synchronized void waitingCall() { try

OC多线程之GCD ----- 2

dispatch_create生成的Queue不管是并行队列还是串行队列,其优先级都是默认优先级 但是可以用dispatch_set_target_queue来改变队列的优先级 dispatch_set_target_queue(原来的队列, 目标优先级队列) 使用这个函数需要获取两个队列,一个是需要变更优先级的队列,一个是指定优先级的队列(指定优先级的队列可以通过get_global获得) 如果多个串行队列优先级相同,那么这些队列里的任务也会串行执行 dispatch_after函数并不能非常

JAVA多线程之wait/notify

本文主要学习JAVA多线程中的 wait()方法 与 notify()/notifyAll()方法的用法. ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 ②wait() 与  notify/notifyAll() 的执行过程 ③中断 调用wait()方法进入等待队列的 线程 ④notify 通知的顺序不能错 ⑤多线程中测试某个条件的变化用 if 还是用 while? ①wait() 与 notify/notifyAll 方法必须在同步代码块中使用 wait()

多线程之AutoResetEvent

我们在线程编程的时候往往会涉及到线程的通信,通过信号的接受来进行线程是否阻塞的操作. AutoResetEvent 允许线程通过发信号互相通信.通常,此通信涉及线程需要独占访问的资源. AutoResetEvent 的方法有很多,具体方法和扩展方法请详见AutoResetEvent类,最常用方法中就有Set()和WaitOne(). 线程通过调用 AutoResetEvent 上的 WaitOne 来等待信号.如果 AutoResetEvent 处于非终止状态,则该线程阻塞,并等待当前控制资源的

Java多线程之wait(),notify(),notifyAll()

在多线程的情况下,因为同一进程的多个线程共享同一片存储空间,在带来方便的同一时候,也带来了訪问冲突这个严重的问题.Java语言提供了专门机制以解决这样的冲突,有效避免了同一个数据对象被多个线程同一时候訪问. wait与notify是java同步机制中重要的组成部分.结合与synchronizedkeyword使用,能够建立非常多优秀的同步模型. synchronized(this){ }等价于publicsynchronized void method(){.....} 同步分为类级别和对象级别