• c#之task与thread区别及其使用


    1.什么是thread
    当我们提及多线程的时候会想到thread和threadpool,这都是异步操作,threadpool其实就是thread的集合,具有很多优势,不过在任务多的时候全局队列会存在竞争而消耗资源。thread默认为前台线程,主程序必须等线程跑完才会关闭,而threadpool相反。
    总结:threadpool确实比thread性能优,但是两者都没有很好的api区控制,如果线程执行无响应就只能等待结束,从而诞生了task任务。
    2.什么是task
     task简单地看就是任务,那和thread有什么区别呢?Task的背后的实现也是使用了线程池线程,但它的性能优于ThreadPoll,因为它使用的不是线程池的全局队列,而是使用的本地队列,使线程之间的资源竞争减少。同时Task提供了丰富的API来管理线程、控制。但是相对前面的两种耗内存,Task依赖于CPU对于多核的CPU性能远超前两者,单核的CPU三者的性能没什么差别。

    3.创建一个task任务有两种模式:
    1.使用factory创建会直接执行,使用new创建不会执行,必须等到start启动之后才执行。


    public void test()
    {
    var testTask = new Task(() =>
    {
    Console.WriteLine("task start");
    });
    testTask.Start();
    var factoryTeak = Task.Factory.StartNew(() =>
    {
    Console.WriteLine("factory task start");
    });
    }


    2.我们来看看task的生命周期


    var testTask = new Task(() =>
    {
    Console.WriteLine("task start");
    System.Threading.Thread.Sleep(2000);
    });
    Console.WriteLine(testTask.Status);
    testTask.Start();
    Console.WriteLine(testTask.Status);
    Console.WriteLine(testTask.Status);
    testTask.Wait();
    Console.WriteLine(testTask.Status);
    Console.WriteLine(testTask.Status);


    输出结果:

    Created
    task start
    Running
    Running
    RanToCompletion
    RanToCompletion

    可以看出task确实是异步执行,并且wait很好地控制了task。

    3.下面演示几个控制task的方法
       

         var testTask = new Task(() =>
                {
                    Console.WriteLine("task start");
                    System.Threading.Thread.Sleep(2000);
                });
      testTask.Start();
    testTask.Wait();

    var testTask = new Task(() =>
                {
                  Console.WriteLine("task start");
                  System.Threading.Thread.Sleep(2000);
                });
      testTask.Start();
    var factoryTeak = Task.Factory.StartNew(() =>
                   {                 
                      Console.WriteLine("factory task start");
                });
                Task.WaitAll(testTask, factoryTeak);
                Console.WriteLine("end");


     var testTask = new Task(() =>
                {
                    Console.WriteLine("task start");
                    System.Threading.Thread.Sleep(2000);
                });
                testTask.Start();
     var factoryTeak = Task.Factory.StartNew(() =>
                   {                  
                      Console.WriteLine("factory task start");
                });
                Task.WaitAny(testTask, factoryTeak);
                Console.WriteLine("end");
    通过wait()对单个task进行等待,Task.waitall()对多个task进行等待,waitany()执行任意一个task就往下继续执行。

    4.task的回调执行

    var testTask = new Task(() =>
    {
    Console.WriteLine("task start");
    System.Threading.Thread.Sleep(2000);
    });
    testTask.Start();
    var resultTest = testTask.ContinueWith<string>((Task) => {
    Console.WriteLine("testTask end");
    return "end";
    });
    Console.WriteLine(resultTest.Result);
    5.task的取消

    首先创建一个取消task的令牌的实例,在不启动task直接取消:

    var tokenSource = new CancellationTokenSource();//创建取消task实例
    var testTask = new Task(() =>
    {
    for (int i = 0; i < 6; i++) {
    System.Threading.Thread.Sleep(1000);
    }
    },tokenSource.Token);
    Console.WriteLine(testTask.Status);
    tokenSource.Token.Register(()=> {
    Console.WriteLine("task is to cancel");
    });
    tokenSource.Cancel();
    Console.WriteLine(testTask.Status);
     输出结果:

    Created
    task is to cancel
    Canceled

    如果task启动了真的取消了task?

    var tokenSource = new CancellationTokenSource();//创建取消task实例
    var testTask = new Task(() =>
    {
    for (int i = 0; i <6; i++) {
    System.Threading.Thread.Sleep(1000);
    }
    },tokenSource.Token);
    Console.WriteLine(testTask.Status);
    testTask.Start();
    Console.WriteLine(testTask.Status);
    tokenSource.Token.Register(()=> {
    Console.WriteLine("task is to cancel");
    });
    tokenSource.Cancel();
    Console.WriteLine(testTask.Status);
    for (int i = 0; i < 10; i++)
    {
    System.Threading.Thread.Sleep(1000);
    Console.WriteLine(testTask.Status);
    }
    输出结果:

    Created
    WaitingToRun
    task is to cancel
    Running
    Running
    Running
    Running
    Running
    Running
    RanToCompletion
    RanToCompletion
    RanToCompletion
    RanToCompletion
    RanToCompletion

     可以看出其实并没有取消task,此时task还在继续跑。

    6.task的嵌套

    var parentTask = new Task(()=> {
    var childTask = new Task(() =>{
    System.Threading.Thread.Sleep(2000);
    Console.WriteLine("childTask to start");
    });
    childTask.Start();
    Console.WriteLine("parentTask to start");
    });
    parentTask.Start();
    parentTask.Wait();
    Console.WriteLine("end");
    此时为普通关联,父task和子task没影响

    var parentTask = new Task(()=> {
    var childTask = new Task(() =>{
    System.Threading.Thread.Sleep(2000);
    Console.WriteLine("childTask to start");
    }, TaskCreationOptions.AttachedToParent);
    childTask.Start();
    Console.WriteLine("parentTask to start");
    } );
    parentTask.Start();
    parentTask.Wait();
    Console.WriteLine("end");
    此时为父task和子task关联,wait会一直等待父子task执行完。

    6.task死锁的问题

    我们可以设置最大等待时间,如果超过了等待时间,就不再等待,下面我们来修改代码,设置最大等待时间为5秒(项目中可以根据实际情况设置),如果超过5秒就输出哪个任务出错了

    7.对Spinlock的使用

    举例来说Parallel.for和Parallel.foreach是线程不安全的,有可能达不到你的预期,此时就需要加锁来解决此问题,我们可以加lock和spinlock(自旋锁)来解决

    SpinLock slock = new SpinLock(false);
    var testLock= new object();
    long sum1 = 0;
    long sum2 = 0;
    long sum3 = 0;
    Parallel.For(0, 100000, i =>
    {
    sum1 += i;
    });

    Parallel.For(0, 100000, i =>
    {
    bool lockTaken = false;
    try
    {
    slock.Enter(ref lockTaken);
    sum2 += i;
    }
    finally
    {
    if (lockTaken)
    slock.Exit(false);
    }
    });
    Parallel.For(0, 100000, i =>
    {
    lock(testLock)
    {
    sum3 += i;
    };
    });
    Console.WriteLine("Num1的值为:{0}", sum1);
    Console.WriteLine("Num2的值为:{0}", sum2);
    Console.WriteLine("Num3的值为:{0}", sum3);
    输出结果:

    Num1的值为:1660913202
    Num2的值为:4999950000
    Num3的值为:4999950000

    Num1的值为:2754493646
    Num2的值为:4999950000
    Num3的值为:4999950000

    Num1的值为:4999950000
    Num2的值为:4999950000
    Num3的值为:4999950000

    最后看看threadpoll和task的结构图:

    threadpool:

     task:


    原文链接:https://blog.csdn.net/qq_40677590/article/details/102797838

  • 相关阅读:
    [一个64位操作系统的设计与实现] 3.1 Func_GetFATEntry疑惑
    【参考】 实现X86_64架构下的BootLoader(二)文件系统
    LBA和CHS转换(转)
    Grafana 重置admin密码
    linux-source: not found ubuntu执行脚本报错
    Hbase学习
    高并发理解
    Linux下安装Artemis
    SpringInAction 第八章 发送异步消息
    SpringInAction 六七章总结
  • 原文地址:https://www.cnblogs.com/huandong963/p/12594064.html
Copyright © 2020-2023  润新知