• C#中的Task:异步编程的瑞士军刀


    在现代软件开发中,异步编程已经成为处理I/O密集型任务和网络操作的重要手段。C#中的Task是.NET Framework 4.0引入的一个并发编程的抽象,它在后续的.NET Core和.NET 5+中得到了进一步的发展和完善。Task代表了一个异步操作,可以等待它的完成,检查它是否已完成,或者取消它。在C#中,Task是一个非常强大的并发工具,使得异步编程变得更加简洁和易于理解。

    1、Task的基本概念

    在C#中,一个Task可以看作是一个异步操作的容器。它提供了一个状态机,可以处于以下几种状态之一:

    • 未启动(Waiting):任务还没有开始执行。
    • 运行中(Running):任务正在执行。
    • 已完成(Faulted/Canceled):任务已经完成,但是有异常抛出或者被取消了。
    • 挂起(Suspended):在.NET 4.5之后引入的状态,表示任务被挂起,等待唤醒。

    2、Task的基本使用

    在C#中创建一个Task的基本语法如下:

    Task<TResult> MyTaskAsync(params object[] parameters)
    {
        // 异步操作...
        return await Task.Run(() =>
        {
            // 具体的异步操作代码
        });
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    使用await关键字可以等待Task完成,并且在Task完成时继续执行后续代码。

    var myTask = MyTaskAsync();
    myTask.Wait(); // 等待Task完成
    // 在这里可以安全地使用myTask.Result来获取结果
    
    • 1
    • 2
    • 3

    3、Task的属性

    IsCompleted:判断Task是否已经完成。
    IsFaulted:判断Task是否因异常而失败。
    IsCanceled:判断Task是否被取消。
    Result:获取Task的结果,仅当Task成功完成时才有值。

    4、Task的方法

    Wait():等待Task完成。
    ContinueWith():当Task完成时,执行一个或多个操作。
    RunAsync():启动一个异步操作。

    5、Task的优点

    简化异步编程: Task让异步编程的模型更加接近同步编程,减少了回调函数和锁的复杂性。
    增强的错误处理: Task提供了异常处理机制,可以通过try-catch块来捕获和处理异步操作中的异常。
    任务取消: 可以使用CancellationToken来取消正在执行的Task。
    任务继续: 可以使用ContinueWith方法来安排一个Task在完成后执行另一个Task。
    并行执行: Task可以利用多核CPU的优势,进行并行计算,提高程序的性能。
    任务等待: 可以使用Task.Wait来等待一个Task完成,或者使用await关键字在异步方法中等待Task的完成。

    6、Task的等待和结果获取

    Task提供了几种方式来等待任务的完成,最常用的是Wait方法和await关键字。

    • Task.Wait():这个方法会导致当前线程阻塞,直到任务完成。

    await关键字: 当你在异步方法中使用await时,你会得到一个Task类型的返回值,这个返回值会在方法中自动等待直到任务完成。

    // 使用Task.Wait()等待任务完成
    task.Wait();
    
    // 使用await关键字等待任务完成
    await task;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    7、Task的异常处理

    Task在完成时可能会抛出异常。这些异常可以通过Task的Exception属性来捕获。

    try
    {
        await task;
    }
    catch (AggregateException ae)
    {
        foreach (var e in ae.InnerExceptions)
        {
            Console.WriteLine($"捕获到异常:{e.Message}");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    8、Task的取消

    使用CancellationToken可以取消一个正在执行的Task。

    CancellationTokenSource cts = new CancellationTokenSource();
    
    var task = new Task(() =>
    {
        while (!cts.Token.IsCancellationRequested)
        {
            // 执行任务
        }
    }, cts.Token);
    
    // 取消任务
    cts.Cancel();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    9、Task的ContinueWith

    ContinueWith方法允许你在一个Task完成后启动另一个Task。

    var parentTask = new Task(() =>
    {
        // 父任务
    });
    
    parentTask.ContinueWith(t =>
    {
        // 父任务完成后执行的代码
    }, TaskScheduler.FromCurrentSynchronizationContext());
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    10、Task的ContinueWith和ContinueWithAsync

    ContinueWith方法允许你在一个Task完成后执行额外的操作,这个方法对于安排任务完成后的清理工作或者日志记录非常有用。你可以在ContinueWith中定义一个回调函数,这个函数会在前一个Task完成后立即执行。

    var task = new Task(() =>
    {
        // 执行一些操作
    });
    
    task.ContinueWith(t =>
    {
        // 当前任务完成后执行的代码
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    在.NET 4.5中,引入了ContinueWithAsync方法,它允许你以异步方式继续执行任务。这非常有用,因为它允许你在不阻塞当前线程的情况下等待任务完成。

    var task = new Task(() =>
    {
        // 执行一些操作
    });
    
    await task.ContinueWithAsync(t =>
    {
        // 以异步方式继续执行
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    11、Task的并行处理

    在.NET 4.0中,Task类提供了一个名为Task.Factory.StartNew的方法,它允许你创建并行执行的任务。从.NET 4.5开始,你可以直接使用Task构造函数来创建并行任务。

    var task1 = new Task(() =>
    {
        // 第一个任务
    });
    
    var task2 = new Task(() =>
    {
        // 第二个任务
    });
    
    // 启动任务
    task1.Start();
    task2.Start();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    12、Task的链式调用

    从.NET 4.6开始,Task支持链式调用,这意味着你可以连续调用ContinueWith或ContinueWithAsync,而不需要每次都创建一个新的Task。

    var task = new Task(() =>
    {
        // 执行一些操作
    }).ContinueWith(t =>
    {
        // 第一个回调
    }).ContinueWith(t =>
    {
        // 第二个回调
    });
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    13、Task的Awaiter

    Task类型实现了INotifyCompletion接口,这允许你使用await关键字来等待一个Task的完成。await关键字背后的实现使用了Task的Awaiter属性。

    var task = new Task(() =>
    {
        // 执行一些操作
    });
    
    await task; // 使用await等待任务完成
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    14、使用Task进行异步I/O操作

    例如,使用Task来读取文件:

    public async Task<string> ReadFileAsync(string filePath)
    {
        using (var reader = new StreamReader(filePath))
        {
            return await reader.ReadToEndAsync();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    15、Task的调度

    默认情况下,Task会在当前线程上运行,但你可以通过TaskScheduler来调度Task在其他线程上运行。

    var ts = new TaskScheduler(/* 线程池或者其他线程 */);
    var task = new Task(MyMethod, state, cancellationToken, creationOptions, scheduler);
    
    • 1
    • 2

    总结

    在C#中,Task是处理异步编程的关键抽象。它提供了一种简单、直观的方式来创建和管理异步操作,并且能够利用现代多核处理器的优势。通过Task,开发者可以更容易地构建高性能、响应式的应用程序。

    在实际应用中,Task的正确使用可以显著提高程序的性能和用户体验。掌握Task的使用,对于任何希望深入理解.NET并发模型的开发者来说,都是非常重要的。

  • 相关阅读:
    【C语言】编译和链接
    Android设计模式--观察者模式
    langchain主要模块(四):Memory
    SQL语句,存储过程,触发器
    Pytorch 的基本概念和使用场景介绍
    MATLAB计算极限和微积分
    手把手教你实现一个JavaWeb项目:创建一个自己的网页博客系统(前端+后端)(一)
    【CVPR 2022 多模态融合(有3D检测)】Multimodal Token Fusion for Vision Transformers
    vue的双向绑定的原理,和angular的对比
    封装localStorage,支持切换存储引擎 sessionStorage,支持vue hook方式调用
  • 原文地址:https://blog.csdn.net/qq_35320456/article/details/137975227