接上一篇,主要写task的使用,没办法发表后再修改搞不好就把文本丢了

 3、类 Task

 参考资料:百度安全验证

请高手们说说Task和Thread的区别_百度知道

面试必备:请问C#中Task和Thread有区别吗?如果有请简述区别_Run

task与thread的区别和使用讲解  https://www.jb51.net/article/250950.htm

3.1、Task的用途

        task是异步编程的的核心,task在线程池的基础上进行了优化,提供了更多的API,Task类专门擅长于异步操作,于是就有了一个问题,thread也是异步编程,那么thread和Task有什么区别呢?为了回答这个问题应该梳理一下Task是如何来的。        

 3.2 、Task的来历

第一步,我们首先有了thread,这个类用以创造一些线程,用以实现异步编程

第二步,在现成的基础上,人们有发明了线程池 Threadpool, Threadpool其实就是thread的集合,具有很多优势,不过在任务多的时候全局队列会存在竞争而消耗资源。

线程池比之thread有许多优点:

thread默认为前台线程,主程序必须等线程跑完才会关闭,而threadpool相反。

但线程池也有如下缺点

  • 不支持线程的取消,就像公园的摇摇车,坐上去后必须等这一圈跑完才能下来。
  • 不支持线程执行的先后次序,这个有点像车站的出租车,坐上去后司机启动车的快慢乘客是无法控制的,因此当任务多的时候全局队列会存在竞争而消耗资源。

第三步,在上述线程池的基础上发展出了Task

task简单地看就是任务,那和thread有什么区别呢?

Task的背后的实现也是使用了线程池线程,但它的性能优于ThreadPoll,因为它使用的不是线程池的全局队列,而是使用的本地队列,使线程之间的资源竞争减少。

同时Task提供了丰富的API来管理线程、控制。

但是相对前面的两种耗内存,Task依赖于CPU对于多核的CPU性能远超前两者,单核的CPU三者的性能没什么差别。

于是我们可以回答3.1提出的问题,thread和Task有什么区别呢?

  • task是根据自己需要调用线程

  • thread就是个基本单位

  • 简单地说,thread是单核多线程,task是多核多线程

  • Task是将多个操作封装成一个概念上原子操作。但这个操作由哪个Thread甚至多个Thread来处理处理你并不清楚。总之就是可以被正常完成。

  • Thread仅仅是一条线程,所有操作都是这个Thread一个人完成的。

  • Task较新,发布于.NET 4.5,能结合新的async/await代码模型写代码,它不止能创建新线程,还能使用线程池(默认)、单线程等方式编程,在UI编程领域,Task还能自动返回UI线程上下文,还提供了许多便利API以管理多个Task。

  • Task默认使用线程池,也就是后台线程:当主线程结束时,你创建所有的tasks都会结束。
  • Task.Run返回一个Task对象,可以使用它来监视其过程
  • 在Task.Run之后,我们没有调用Start,因为该方法创建的是“热”任务(hot task)
  • 可以通过task的构造函数创建“冷”任务(cold task),但开发中很少这么干
  • 通过Task的Status属性来跟踪task的执行状态。


Task属于多核开发的封装。跟单线程工作的任务有很大的区别,甚至是本质上的区别。所以可比性不大。

3.3、Task的使用方法 

        Task类的使用与委托密不可分,就像delegate委托用泛型来实现时根据被委托的函数是否有返回值可以分为Action<...>和Func<...,Tresult>两种,Task类在使用时叶根据对应的委托是否有返回值分为两种:

Task类  对应void方法Task(Action)
Task<TResult>有返回值的方法Task<TRsult>(Func<TResult>)

Task taskA = Task.Run( () => Thread.Sleep(2000));

(1)无参数Task类

        这种一共有以下几种使用方法

using System;
using System.Threading;
using System.Threading.Tasks;

class Example
{
    static void Main()
    {
        Action<object> action = (object obj) =>
                                {
                                   Console.WriteLine("Task={0}, obj={1}, Thread={2}",
                                   Task.CurrentId, obj,
                                   Thread.CurrentThread.ManagedThreadId);
                                };

        // t1这个任务是用声明一个Task类的实例的方法instantiated。 
        Task t1 = new Task(action, "alpha");

        // t2这个任务实现用了Task.Factory.StartNew(action, "beta");这个语句。
        Task t2 = Task.Factory.StartNew(action, "beta");
        // Block the main thread to demonstrate that t2 is executing
        t2.Wait();//这里代表着要等t2结束再往下运行

        // 运行 t1 
        t1.Start();
        Console.WriteLine("t1 has been launched. (Main Thread={0})",
                          Thread.CurrentThread.ManagedThreadId);
        // Wait for the t1 task to finish.
        t1.Wait();

  // Construct a started task using Task.Run.用Task.Run这个语句构建了一个已经开始的任务t3;
        String taskData = "delta";
        Task t3 = Task.Run( () => {Console.WriteLine("Task={0}, obj={1}, Thread={2}",
                                                     Task.CurrentId, taskData,
                                                      Thread.CurrentThread.ManagedThreadId);
                                   });
        // Wait for the task t3  to finish.
        t3.Wait();

        // Construct an unstarted task
        Task t4 = new Task(action, "gamma");
        // Run it synchronously
        t4.RunSynchronously();
        // Although the task was run synchronously, it is a good practice
        // to wait for it in the event exceptions were thrown by the task.
        t4.Wait();
    }
}
// The example displays output like the following:
//       Task=1, obj=beta, Thread=3
//       t1 has been launched. (Main Thread=1)
//       Task=2, obj=alpha, Thread=4
//       Task=3, obj=delta, Thread=3
//       Task=4, obj=gamma, Thread=1

  由上面的一段程序可以知道,使用无参数的task非常简单:

 Task t1 ;

  public static void metha()
  {         
          Console.WriteLine("zzzzzzzzzzz");          
  }

 Action A = metha;      //无参无返

//===================================================

     方法1 :  t1= Task.Run(A);                                  
     方法2:   t1 = Task.Run(() => { Console.WriteLine("hello"); });
     方法3 :   t1 = Task.Run(() => metha());
     方法4 :   t1 = new Task(A);

(2)有参数的Task

这里主要说的是在运行的方法有参数时如何使用Task.Run()方法打开一个任务

t这个方法比较多的用到了lamda表达式,所以先看一下lamda表达式的用法

lamda表达式实际上是一种匿名函数,=>这个符号叫做lamda运算符,

var     t2= Task.Run( () =>

           {
                Console.WriteLine("task start");
            }

);

观察下,run后边括号里的部分变成了

()   =>   {  Console.WriteLine("task start"); }

lamdba后边的中括号前是不是少了函数名?这就是匿名函数。

来看一下,lamdba表达式后边是函数名,注意到差别了吗?

var A = Task.Run(() => R());
            
//========================================================//

static void R()
{
       。。。
}

于是联系以下方法的声明,前边括号里表示的应该时形参,于是可以知道,空括号表示0个输入函数。下面一段代码实现了一些lamda表达式情况下task.Run()的用法。


static void HaveParam(string  a)
{
       Console.WriteLine(a);
}



Task t2 = Task.Run(() => HaveParam("hello,word"));//这里就不能用提前做的委托了                                                                   


Action<string > bcc = (string obj) =>
{
       Console.WriteLine(obj);
};
Task t4 = new Task((Action<object>)bcc, "have");



Action<string> action_Have = HaveParam;//有参无返
t4 = new Task((Action<object>)action_Have, "have");



Action<object> acc = (object o) =>
{
        Console.WriteLine(o);
};
t4 = new Task(acc, "a");
t4 = new Task(acc, 1);

这种情况下限制就有点多了,能用的只有方法3和方法4了,这中间比较常用的是方法3,方法4应该注意用的时候形参的数据格式用object,用其他的还得转一下数据格式

     方法3 :   t1 = Task.Run(() => metha());
     方法4 :   t1 = new Task(A);

 以上是指只有单个的参数的情况,看task构造函数的定义会发现,方法4只能适用于只有一个参数的情况,所以当需要传递的参数大于1时,就只能用方法3,这就是说对于task.run()来说,括号里边的委托要么在括号里完成,要么就在外边把参数传递好,总之,不要向task里边放一个还没传递好参数的且已经是成品的委托,run()的括号里边是不适合向委托传递函数的。

   方法3 :   t1 = Task.Run(() => metha());

方法3就是,向函数metha()里边传递函数,传递完了再通过lamdba表达式转变为委托,于是括号里边是一个传递好函数的委托,

如果非要用方法4的话,A应该是一个传递好参数的委托,

将函数比作一个礼物,必须在完全做好后才能放进礼物盒(委托)中,然后再将礼物送给快递员(Task.Run())。就像下边,方法prinf在通过lamdba包装好放进aaa前,就完成了参数传递。给task.run()的是一个完整的盒子。

       
        Action aaa =()=> prinf("a");        
        Task tttt = Task.Run(aaa);


        Task ss = Task.Run(() => prinf ("aaa"));

        Task s = Task.Run(() => Console.WriteLine("hello"));

        
        static void prinf(string a)
        {
            Console.WriteLine(a);
        }

这个来源于task这个api的编写,除非重写api,否则也就这个功能了。

到这里无返回值的task几乎完毕了,剩下的就是t.wait();t.result等等,这些就自己看一下微软的定义就好。

Task Class (System.Threading.Tasks) | Microsoft Docs

 (3)有返回值的task

Task

由上可知,我可以不再理会方法4,只用方法3的话这里非常简单

 var t = Task<int>.Run(() => {                
                int max = 1000000;               
                return max;
            });
Console.WriteLine("Finished {0:N0} iterations.", t.Result);
            
var Rstring  = Task.Run(() => R_P("string"));

Rstring = Task.Run(() => R());
            

//========================================================//
static string  R_P(string a)
{
            return a;
}
static string R()
{
            string a = "hello";
            return a;
}

这里需要注意的是数据类型,c#语言是一个强类型函数在这里表现得非常明显,当使用var来声明一个变量t,这个t就可以有result,也就是返回值,但如果用task来声明t那么无法取得返回值

右边的确返回了一个Task类型的值,但在这里最好使用var,var可以用task的功能,比如

var t,t可以这样:t.wait();

也可以t.result;

当然如果使用task的话,可以在前面声明一个Task a,然后a可以放在任何一个等式的左边

如果使用一个var的话,就像上面的程序

var t中的t接了一个int型的值,于是t就成了int型的变量,t不能代替Rstring,但Rstring可以放在第2.3个等式前边,因为在第二个等式中Rstring成了string型的变量,第三个等式的返回值也是string,于是可以使用。

其余的东西大同小异可以自己看说明书。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值