C# Task类- 多线程学习2

上一篇文章(C# Thread类 - 多线程学习1)说过多线程之Thread 类,下面继续学习多线程的另一个更重要的知识点:Task 类,如果有时间也建议看下上面的这篇文章,或许有用呐。

Thread线程是用来创建并发的一种低级别工具,它具有一些限制,尤其是:

  • 虽然开始线程的时候可以方便的传入数据,但是当join的时候很难从线程获得返回值。
  • 可能需要设置一些共享字段。
  • 如果操作抛出异常,铺货和传播该异常都很麻烦
  • 无法告诉线程在结束时开始另外的工作,你必须进行join操作(在进程中阻塞当前的线程)
  • 很难使用较小的并发(concurrent)来组件大型的并发

Task类可以很好的解决上述问题,它是一个高级抽象:它代表了一个并发操作(concurrent),该操作可能有Thread支持,或不由Thread支持。

  • Task是可组合的(可使用continuation把他们穿成链)。
  • Tasks可以使用线程池来减少启动延迟。
  • 使用TaskCompletionSource,Tasks可以利用回调的方式,在等待I/O绑定操作时完全避免使用线程。

Task建立多线程有多种方法,下面通过代码进行说明。代码可能有点长,但是把它分块就比较好理解的,因为是多种方法合并在一起进行介绍的,所以整个代码与输出结果比较长。

直接上代码,后面有对代码的解析:

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

namespace ch_Task
{
    
    
    class Program
    {
    
    
        static void CallTask2(string name)
        {
    
    
            Console.WriteLine($"taskID:{
      
      Task.CurrentId} beginning! ---name:" + name);
            for (int i = 1; i <= 5; i++)
            {
    
    
                Thread.Sleep(500);
                Console.WriteLine($"{
      
      i.ToString()} ---name:task2");
            }
            Console.WriteLine($"taskID:{
      
      Task.CurrentId} finished! ---name:" + name);
        }
        public static void CallTask3()
        {
    
    
            Console.WriteLine($"taskID:{
      
      Task.CurrentId} beginning! ---name:task3");
            for (int i = 1; i <= 5; i++)
            {
    
    
                Thread.Sleep(500);
                Console.WriteLine($"{
      
      i.ToString()} ---name:task3");
            }
            Console.WriteLine($"taskID:{
      
      Task.CurrentId} finished! ---name:task3");
        }
        public static void CallTask3Continue(Task t)
        {
    
    
            Console.WriteLine($"taskID:{
      
      Task.CurrentId} beginning! ---name:task3.continue");
            for (int i = 1; i <= 5; i++)
            {
    
    
                Thread.Sleep(500);
                Console.WriteLine($"{
      
      i.ToString()} ---name:task3.continue");
            }
            Console.WriteLine($"taskID:{
      
      Task.CurrentId} finished! ---name:task3.continue");
        }

        async static void task4()
        {
    
    
            Console.WriteLine($"taskID:{
      
      Task.CurrentId} beginning! ---name:task4");
            for (int i = 1; i<= 5; i++)
           {
    
    
              Console.WriteLine($"{
      
      i.ToString()} ---name:task4");
              await Task.Delay(500);
            }
            Console.WriteLine($"taskID:{
      
      Task.CurrentId} finished! ---name:task4");
        }

        static void Main(string[] args)
        {
    
    
            //-----------方式1-----------------
            Task task1 = new Task(() =>
            {
    
    
                Console.WriteLine($"taskID:{
      
      Task.CurrentId} beginning! ------name:task1");
                
                for(int i=1;i<=5;i++)
                {
    
       
                    Thread.Sleep(500);
                    Console.WriteLine($"{
      
      i.ToString()} ---name:task1");
                }
                Console.WriteLine($"taskID:{
      
      Task.CurrentId} finished! ------name:task1");
            });
            task1.Start();

            // program execution after task1 finish
            task1.ContinueWith((task) =>
            {
    
    
                Console.WriteLine($"taskID:{
      
      task.Id}.continue begin and this task id is {
      
      Task.CurrentId}");
               
                for (int i = 1; i <= 5; i++)
                {
    
     
                    Thread.Sleep(500);
                    Console.WriteLine($"{
      
      i.ToString()} ---name:task1.continue");
                }
                Console.WriteLine($"taskID:{
      
      Task.CurrentId} finished! ------name:task{
      
      task.Id}.continue");
            });

            //-------------方式2----------------
            var task2 = new Task(() => CallTask2("Task2"));
            task2.Start();

            //------------方式3------------
            Task task3 = new Task(CallTask3);
            task3.Start();
            Task task3Continue = task3.ContinueWith(CallTask3Continue);

            task3Continue.Wait(); // 阻塞主线程,直到task3Continue 线程执行结束
            //-------------方式4-------------
            task4();

            Console.ReadKey();
        }
    }
 }

输出结果为:

taskID:1 beginning! ------name:task1
taskID:3 beginning! ---name:Task2
taskID:4 beginning! ---name:task3
1 ---name:task1
1 ---name:task2
1 ---name:task3
2 ---name:task1
2 ---name:task2
2 ---name:task3
3 ---name:task1
3 ---name:task2
3 ---name:task3
4 ---name:task1
4 ---name:task3
4 ---name:task2
5 ---name:task1
taskID:1 finished! ------name:task1
taskID:1.continue begin and this task id is 2
5 ---name:task3
5 ---name:task2
taskID:4 finished! ---name:task3
taskID:3 finished! ---name:Task2
taskID:5 beginning! ---name:task3.continue
1 ---name:task1.continue
1 ---name:task3.continue
2 ---name:task1.continue
2 ---name:task3.continue
3 ---name:task3.continue
3 ---name:task1.continue
4 ---name:task3.continue
4 ---name:task1.continue
5 ---name:task3.continue
5 ---name:task1.continue
taskID:5 finished! ---name:task3.continue
taskID:2 finished! ------name:task1.continue
taskID: beginning! ---name:task4
1 ---name:task4
2 ---name:task4
3 ---name:task4
4 ---name:task4
5 ---name:task4
taskID: finished! ---name:task4

下面对输出结果进行分析:
直接看主函数部分,可以看到共有task1、task1.continue、task2、task3、task3.continue、task4六个子线程、分别用了四种方法:

task1 使用 : Task task1 = new Task(() =>{
    
     } );直接去创建子线程。其实,这个运行的话可以简化即:
将下面代码:

Task task1 = new Task(() =>{
    
     } );
task1.start()

换成 : Task.Run(() =>{
    
     } ); 即可
task2 使用: var task2 = new Task(() => CallTask2("Task2")) 创建子线程。
task3 使用: Task task3 = new Task(CallTask3);直接创建子线程
task4 使用异步方法去创建子线程,关键词:async, 可以看到在task4之前,使用了task3Continue.Wait();  
这个语句可以用来阻塞主线程,直到task3Continue 线程执行结束后才会 task4线程的定义与运行。

另外,可以看到,在别的task使用延时用的都是 Thread.Sleep(500); 而使用异步方法创建的子线程在使用延时
的时候使用的是 await Task.Delay(500);这一点需要留意。
task1.continue 线程 即 task1.ContinueWith((task) => {
    
    })在task1 完全执行结束后才执行,
同理
task3.continue在task3 完全结束之后才执行。

Task.ContinueWith((task) => {
    
    })可以将子线程串联起来

猜你喜欢

转载自blog.csdn.net/ZhangJingHuaJYO/article/details/124364408