首页 > 编程语言 >c#中的异步操作 task与async/await

c#中的异步操作 task与async/await

时间:2024-06-11 15:45:16浏览次数:26  
标签:异步 Task c# await task 操作 async 方法

Task

Task 类表示一个异步操作。这个操作可以通过 Task.Run 方法、TaskFactory.StartNew 方法,或直接通过 new TaskTask.Start 来启动。

Task.Run举例

Task task = Task.Run(() => {
    // 执行一些长时间运行的操作
});

Task.Run() 是一个用来执行异步操作的方法。它启动一个新的任务(Task),这个任务在后台线程上执行指定的代码块。这样,它可以使得CPU密集型或者长时间运行的任务不会阻塞主线程。

 

await Task.Run(() => { });

如果在不需要异步操作的执行结果的情况下,可以使用await Task.Run(() => { });

Task.Run() 自身并不涉及阻塞;它仅仅是启动了一个任务。这时,主线程(UI 线程)将在启动任务后立即继续执行后续的代码。这种情况下,虽然 UI 不会因为后台任务而阻塞,但如果主线程依赖于这个任务的结果来更新 UI 或进行其他操作,可能会遇到问题(如访问未完成计算的数据,导致错误或不一致的状态)。

如果需要在任务完成后执行一些操作(如更新 UI),应该使用 await 来确保这些操作在任务完成后正确执行。

举例说明,考虑以下代码:

// 假设这是一个按钮点击事件处理器
private void buttonStart_Click(object sender, EventArgs e)
{
    Task task = Task.Run(() =>
    {
        // 执行一些长时间运行的操作
        LongRunningOperation();
    });

    // 这里的代码会在 LongRunningOperation 启动后立即执行
    UpdateUI("任务已启动,正在执行中...");
}

在这个例子中:

  • LongRunningOperation() 在后台线程中执行,不会阻塞 UI 线程。
  • UpdateUI("任务已启动,正在执行中..."); 被立即执行,因为主线程没有等待后台任务完成。

如果你需要在后台任务完成后进行操作(如再次更新 UI),你应该使用 await 确保操作在任务完成后执行:

private async void buttonStart_Click(object sender, EventArgs e)
{
    await Task.Run(() =>
    {
        // 执行一些长时间运行的操作
        LongRunningOperation();
    });

    // 任务完成后执行
    UpdateUI("任务已完成!");
}

 


使用 Task<TResult> 类来表示返回结果的异步操作

可以使用 Task<TResult> 类来表示能返回结果的异步操作

Task<int> task = Task.Run(() => {
    return 1 + 1;
});

要等待 Task 完成并获取结果,你可以使用 Result 属性,但这会阻塞当前线程:

int result = task.Result;

 当你调用 task.Result,如果 Task 尚未完成,当前线程将会阻塞,等待 Task 完成。这种行为在某些情况下(尤其是在 UI 线程中)是不可取的,因为它会冻结应用程序的响应,直到任务完成。

 

使用 await 关键字的改进:

使用 await 关键字可以更优雅地处理异步任务的结果,同时避免阻塞主线程。如果我们将第二段代码改为使用 await,看起来会是这样:

int result = await task;

这样改写后的代码具有以下特点:

  • 非阻塞行为:使用 await 会暂停当前方法的执行直到 Task 完成,但它不会阻塞线程。在此期间,线程可以执行其他工作,如响应 UI 事件。
  • 异常处理:使用 await 还可以更简单地处理任务中可能抛出的异常。如果 Task 执行过程中出现异常,使用 await 可以直接在 try...catch 结构中捕获这些异常,而使用 Result 则需要额外的处理。

使用 await 比直接访问 Result 属性更符合异步编程的理念,因为它保持了应用的响应性,并且在语法上更加清晰和易于维护。await 使得异步编程模式更加接近传统的同步编程模式,简化了代码的可读性和异常处理。在实际应用中,推荐使用 await 来处理异步任务,尤其是在 UI 相关的应用程序中,以避免界面的冻结和延迟。

 

async方法

await 关键字只能在由 async 关键字修饰的方法中使用。

  • 声明:当你在方法的返回类型前添加 async 关键字时,你声明了一个异步方法。这个关键字启用了该方法中 await 表达式的使用,并表明该方法是异步的,通常会涉及到耗时操作的异步处理。
  • 返回类型async 方法可以有三种返回类型:
    • Task:用于没有返回值的异步操作。
    • Task<T>:用于有返回值的异步操作,其中 T 是返回值的类型。
    • void:不推荐使用,通常只用于事件处理器中。因为它不支持等待该方法的完成,也不能捕获其中的异常。

 

(注意:在 C# 中,如果你声明了一个返回 Taskasync 方法,你实际上不需要在方法中显式地使用 return 语句来返回一个 Task 对象。这是因为 async 关键字允许编译器为你处理返回 Task 的细节。

当你使用 async 关键字声明方法时,编译器会生成额外的代码,这些代码包括自动创建和返回一个 Task 对象。并确保该 Task 在方法执行完毕时正确地完成。这个过程是自动的,不需要你手动编写任何 return 语句。

如果方法正常完成,生成的 Task 会自动标记为已完成;如果方法中抛出异常,生成的 Task 会捕获这个异常,并在 Task 上反映出相应的异常状态。

async Task<T> 方法:对于返回具体结果的异步方法(返回 Task<T>),你需要使用 return 语句来提供一个类型为 T 的结果。编译器需要这个返回值来完成返回的 Task<T>

 

asyncawait 关键字使得异步代码更易于阅读和维护。标记为 async 的方法会返回一个 TaskTask<T> 对象,你可以在该方法中使用 await 关键字来等待异步操作完成。

public async Task DoSomethingAsync()
{
    // 异步执行某个方法,并等待其完成
    await SomeMethodAsync();
    
    // 上面的异步操作完成后,继续执行
}

await 会非阻塞地等待 Task 完成:它会释放当前线程,使其可用于其他操作,直到等待的 Task 完成。这样,你就可以在 UI 线程中安全地等待长时间运行的操作,而不会冻结用户界面


示例

首先在事件处理方法(定义为async才能调用await类型方法)中,调用异步方法await SendHexFramesAsync(cts_saveHex.Token)

        //定义了一个 CancellationTokenSource 类型的变量 cts_saveHex。这个类用于生成一个或多个 CancellationToken 对象,这些对象可以传递给可以被取消的异步方法。
        CancellationTokenSource cts_saveHex;

        //存储Flash按钮点击事件
        private async void btn_saveFlash_Click(object sender, EventArgs e)
        {
            if (ComDevice.IsOpen)
            {
                //创建了一个新的 CancellationTokenSource 实例,并将其赋值给 cts_saveHex 变量。
          就可以使用 cts_saveHex.Token 获取一个 CancellationToken,并传递给可取消的异步方法。 cts_saveHex = new CancellationTokenSource(); try { //执行 SendHexFramesAsync 异步方法,并传递 cts_saveHex.Token 作为参数。
            这样,如果在 SendHexFramesAsync 方法内部检查了这个 token,并在其被取消时抛出了一个异常,你就可以在外部捕获这个异常并进行相应的处理。 await SendHexFramesAsync(cts_saveHex.Token); } catch (OperationCanceledException) { ButtonAction("open"); // 处理取消操作后的清理工作 if (language == "Chinese") listBox_Information.Items.Add("【" + DateTime.Now.ToString("HH:mm:ss:fff") + "】:已结束存储Hex任务"); else listBox_Information.Items.Add("[" + DateTime.Now.ToString("HH:mm:ss:fff") + "]:ended storing Hex task"); } //Timer_save.Enabled = true; } }

SendHexFramesAsync方法如下,有一个循环,在每次发送报文之后,非阻塞地等待:

//存储——异步发送hex
        private AutoResetEvent autoResetEvent = new AutoResetEvent(false);
        public async Task SendHexFramesAsync(CancellationToken cancellationToken)
        {
                        for (int i = 0; i < blockInfo.DataSegments.Count; i++)
                        {
                            byte[] sendData2 = null;
                            sendData2 = strToHexByte(realData1.Trim());
                            SendData(sendData2);//发每帧数据

                            //3 块首帧的计时器
                            timer_timeout.Interval = 10000;
                            timer_timeout.Enabled = true;
                            timer_timeout.Start();

                            // 1等待接收到回应
                            await Task.Run(() => autoResetEvent.WaitOne());//2检查是否请求了取消操作。如果有抛出一个OperationCanceledException异常
                            cancellationToken.ThrowIfCancellationRequested();
                        }
        }

 

接收数据方法里,如果接收成功:

autoResetEvent.Set();  // 设置信号,以便 SendFramesAsync 可以继续发送下一帧

如果失败或超时:

cts_saveHex.Cancel();
autoResetEvent.Set();  // 设置信号,以便 SendFramesAsync 可以继续执行查询是否取消

 

标签:异步,Task,c#,await,task,操作,async,方法
From: https://www.cnblogs.com/ban-boi-making-dinner/p/17673675.html

相关文章

  • WebSocket
    WebSocket介绍WebSocket是基于TCP的一种新的网络协议。它实现了浏览器与服务器全双工通信——浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,并进行双向数据传输。HTTP协议和WebSocket协议对比:HTTP是短连接WebSocket是长连接HTTP通信是单向的,基于请......
  • 原来可以免费申请Salesforce AI账号?赶紧收藏!
    2024年3月,备受期待的SalesforceGPTAI产品终于进入了各个组织,TrailblazerDX上的生态系统一片沸腾。其中Copilot和Einstein1Studio作为已发布的两款产品引领了潮流。EinsteinCopilot是Salesforce下一代产品的核心,这是一款先进的AI助手,可无缝集成到CRM应用程序中。通过利用自......
  • Socat多功能网络工具
    Socat多功能网络工具socat的启用:与nc相同①基本使用方法一、监听/聊天工具1、Windows做客户端,kali做服务端服务端kali开放7777号端口让客户端Windows7连接(谁做服务端,谁就要加stdout)Windows7连接Kali的7777端口2、Windows做服务端,kali做客户端②基本使用方法二、正/反......
  • Lucene的IK分词器学习,增加支持单个特殊符号搜索
    前言感谢CSDN这篇文章,原始代码基于这里。正常对于“[email protected]”这段文字,搜索'@'这个符号是搜不出来的。本文主要修改是扩展IK分词器,增加了对诸如"@-"这种特殊文字的检索。当然这个其实并没有多少实际意义,所以基本也是出于学习的目的。正文IK分词器分析这里不深入原理,......
  • 使用python处理excel数据
    使用python处理excel数据python处理excel数据时间差计算平均量计算excel处理后数据python处理excel数据excel数据有一列是开始时间,一列为结束时间,计算时间差,时间差>1h,将数据平均为1h。时间差>1h,总量也将平均到每个小时,如第三行数据,时间差为4h,数据为5.2,所以每小时......
  • 理解dispatch_async
    Submitsablockforasynchronousexecutiononadispatchqueueandreturnsimmediately.提交一个块以在调度队列上异步执行并立即返回。codeshowing以一个最简单的demo开始//创建一个同步队列dispatch_queue_tsyncQueue=dispatch_queue_create("io.sqi.My......
  • 细说ARM MCU的串口接收数据的实现过程
    目录一、硬件及工程1、硬件2、软件目的3、创建.ioc工程二、代码修改1、串口初始化函数MX_USART2_UART_Init()(1)MX_USART2_UART_Init()串口参数初始化函数(2)HAL_UART_MspInit()串口功能模块初始化函数2、串口中断的执行过程3、启动串口接收中断4、自动生成main函数5......
  • 巧妙使用mapstruct来解决数据库entiy到实体dto的映射关系
    1.引入mapstruct<dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct</artifactId></dependency><dependency><groupId>org.mapstruct</groupId><artifactId>mapstruct-p......
  • 【问题】--vscode快捷键
      (1)alt+“鼠标单击”:实现多行任意位置同时操作      (2)Ctrl+Alt,再按向上或 向下 的键, 进行多列操作。(用的少)         (3)shift+Alt,按住用鼠标左键拖动, 进行多列操作。       ......
  • docker 的代理设置
    Docker的代理设置有时候当我们设置了镜像仓库之后,依然有些镜像无法直接拉去。另外一个情况就是在buildimage的时候,有些包或者有些依赖的url无法访问。例如国外的一些资源,这时候我们就可以通过配置proxy进行获取。配置docker的proxy路径一般是~/.docker/config.json"proxies"......