c#中的超时终止

在C#中,可以使用CancellationTokenTask的超时机制来实现调用方法时的超时终止。

用Task.Delay(int)模拟耗时操作

        static async Task Main(string[] args){using (var cts = new CancellationTokenSource(1 * 1000)){await doSomething(cts.Token);}Console.WriteLine("Press any key to end...");Console.ReadLine();Console.WriteLine();}static async Task doSomething(CancellationToken cancellationToken){try{  int x = new Random().Next(4, 7);Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} 大约需要{x}秒才能执行结束");//模拟耗时操作await Task.Delay(x * 1000, cancellationToken); Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} 执行doSomething结束");}catch (OperationCanceledException ex){//如果被取消,则抛出异常Console.WriteLine($"{DateTime.Now.ToString("HH:mm:ss.fff")} 超时了:" + ex.Message);}}

二 

        如果我们要设置超时的方法本身不支持异步或超时参数时,可以通过使用TaskCancellationToken来实现,但这需要一些间接的方式。       

        假设,方法doOperate(定义如下)执行时间较长,我们要在调用它时,设置超时操作。

 static bool doOperate(int x, string y, out string error)  {  // 模拟长时间操作  System.Threading.Thread.Sleep(5000); // 假设操作需要5秒  error = null; // 假设没有错误  Console.WriteLine($"Hello {y}");return true; // 假设操作成功  }  

        从doOperate的定义中,我们可以看到:doOperate方法并没有设计为异步且不接受超时或取消令牌。

        这种情况下,如果我们想设置在调用doOperate方法时超时,一种常用的方法是将doOperate的调用放在一个单独的任务中,并使用Task.DelayTask.WhenAny来等待doOperate或超时时间结束。

        这里我们可以使用Task.Run来封装doOperate的调用。

        但请注意,这可能会引入线程池的使用,如果doOperate是CPU密集型的操作,可能会影响系统性能。如果doOperate主要是IO操作(比如文件访问或数据库查询),这种方法的影响会比较小。

static void Main(string[] args){MethodX();}static void MethodX(){string error = null;bool result = CallDoOperateWithTimeout(3*1000, out error);if (!result){Console.WriteLine($"Operation failed: {error ?? "Timeout occurred."}");}else{Console.WriteLine("Operation completed successfully.");}}static bool CallDoOperateWithTimeout(int timeoutMilliseconds, out string error){var cts = new CancellationTokenSource(timeoutMilliseconds);string tempError = null;bool iResult = false;Task task = Task.Run(() =>{try{doOperate(42, "someInput", out tempError);iResult = true;}catch (Exception ex){tempError = ex.Message;iResult = false;}}, cts.Token);try{task.Wait(cts.Token); // 等待任务完成或抛出异常  error = tempError;return iResult;}catch (OperationCanceledException){error = "Operation timed out.";return false;}catch (Exception ex){error = ex.Message;return false;}}

        执行后,我们会发现,确实提示”Operation failed: Operation timed out.“,但是也打印了”Hello somInput“——即doOperate方法并没有被终止掉,这是因为:

        doOperate 方法本身并不是真正的异步方法(即,它并没有使用 async 关键字,也没有 await 任何异步操作)。相反,它使用了 Thread.Sleep 来模拟长时间的操作,这是一个阻塞调用,会阻塞执行它的线程直到指定的时间过去。

        当我们从 Main 方法或任何其他同步上下文中启动这个 Task 时,虽然 Task 本身是在一个单独的线程上执行的,但 doOperate 方法内的 Thread.Sleep 会阻塞那个单独的线程。与此同时,Main 方法中的代码会继续执行到 task.Wait(cts.Token),它等待 Task 完成或超时。

        如果 Task(即 doOperate 方法的执行)在超时之前还没有完成(在这个例子中是5秒),CancellationTokenSource 的 Token 将会被触发来请求取消操作。但是,请注意,doOperate 方法内部并没有检查 CancellationToken 的状态,因此它不会提前退出。因此,Thread.Sleep 将会继续执行直到其完成,随后 doOperate 方法将输出 "Hello {y}" 并返回 true

        然而,在 Main 方法中,由于已经超过了超时时间,task.Wait(cts.Token) 会抛出一个 OperationCanceledException(或者,如果 Task 实际上在超时之后完成了,则不会抛出异常,但在这个例子中它不会)。这个异常会被捕获,并且错误消息会被设置为 "Operation timed out."

        要解决这个问题并让 doOperate 方法能够响应取消请求,您需要在 doOperate 方法内部定期检查 CancellationToken 的状态。

        但是,由于 doOperate 使用了 Thread.Sleep,而 Thread.Sleep 是不支持取消的,您需要使用其他方法来模拟异步操作,比如使用 Task.Delay(它可以接受一个 CancellationToken)或者实现您自己的异步逻辑(比如轮询某种条件或等待某个事件)。

        然而,在这个特定的例子中,由于 doOperate 方法是同步的并且使用了 Thread.Sleep,所以我们无法直接让它响应取消请求。

        如果我们想要让 doOperate 能够被取消,我们需要重写doOperate 以使用异步模式,或者找到一种方法来避免在需要响应取消的场景中使用 Thread.Sleep

        如果我们只是想在超时后停止等待 doOperate 的结果,并且不关心 doOperate 方法是否实际完成,那么我们的代码已经按预期工作了,只是 doOperate 会在后台继续执行直到完成。如果我们想要确保 doOperate 在超时后被取消(即停止执行),那么我需要重新设计 doOperate 方法以支持取消。

        重写doOperate 方法,将它转变为一个异步方法,并使用 CancellationToken 来检查是否应该提前退出。

        然而,由于原始的 doOperate 方法使用了 Thread.Sleep 来模拟长时间操作,这是不可取消的,我们需要找到一个替代方案。

一个常见的替代方案是使用 Task.Delay,它是一个可取消的异步延时操作。下面是重写后的 doOperate 方法和相应的调用逻辑:

    /// <summary>/// 封装操作结果的类/// </summary>private class OperationResult{/// <summary>/// 执行成功/// </summary>public bool Success { get; set; }/// <summary>/// 执行发生异常时的错误消息/// </summary>public string Error { get; set; }}/// <summary>/// 异步版本的doOperate方法/// </summary>/// <param name="x"></param>/// <param name="y"></param>/// <param name="cancellationToken"></param>/// <returns></returns>static async Task<OperationResult> doOperateAsync(int x, string y, CancellationToken cancellationToken){try{// 使用Task.Delay模拟异步操作,该操作支持取消  await Task.Delay(5000, cancellationToken);// 假设这是耗时的异步操作  Console.WriteLine($"Hello {y}");return new OperationResult { Success = true, Error = null };}catch (OperationCanceledException){// 如果操作被取消  return new OperationResult { Success = false, Error = "Operation was cancelled." };}catch (Exception ex){// 捕获并处理其他可能的异常  return new OperationResult { Success = false, Error = ex.Message };}}
static async Task Main(string[] args){await MethodXAsync();Console.WriteLine("press any key to end..."); Console.ReadKey();}static async Task MethodXAsync(){//设置超时时间是3秒OperationResult result = await CallDoOperateWithTimeoutAsync(3 * 1000);if (!result.Success){Console.WriteLine($"Operation failed: {result.Error ?? "Timeout occurred."}");}else{Console.WriteLine("Operation completed successfully.");}}static async Task<OperationResult> CallDoOperateWithTimeoutAsync(int timeoutMilliseconds){var cts = new CancellationTokenSource(timeoutMilliseconds);try{// 注意:这里我们不需要将cts.Token传递给Task.Run,  // 因为我们是在等待DoOperateAsync的完成,而不是Task.Run的完成。  // Task.Run主要用于在后台线程上执行代码,但在这里我们直接调用异步方法。  var result = await doOperateAsync(42, "someInput", cts.Token);return result;}catch (TaskCanceledException){return new OperationResult { Success = false, Error = "Operation timed out." };}catch (Exception ex){return new OperationResult { Success = false, Error = ex.Message };}}

请注意以下几点:

  1. 我将 Main 方法改为异步的,并使用了 await 关键字来等待 MethodXAsync 的完成。这是处理异步程序的常见做法。

  2. CallDoOperateWithTimeoutAsync 方法直接调用 doOperateAsync 并等待其完成,同时处理可能的取消异常和其他异常。doOperateAsync 方法现在是一个返回 OperationResult 实例的异步方法,该实例包含了操作的成功状态和可能的错误消息。

  3. DoOperateAsync 方法现在是一个异步方法,可以在不阻塞当前线程的情况下执行长时间的操作。
    如果 doOperateAsync 中的代码需要在另一个线程上执行(例如,因为它执行了阻塞的 I/O 操作),那么我们可以考虑使用 Task.Run 来封装这部分代码。但是,在这个例子中,Task.Delay 已经是一个异步操作,所以我们不需要额外的线程。

  4. 我创建了一个 OperationResult 类来封装 doOperate 方法的成功状态和可能的错误消息。这样,我们就可以在异步操作完成后返回一个包含这些信息的单一对象。

        现在,当我们运行这个程序时,如果 doOperateAsync 方法在超时之前完成,它将输出 "Hello someInput" 并报告成功。如果超时发生,它将报告超时错误,并且 doOperateAsync 方法中的 Console.WriteLine 将不会被执行(因为 Task.Delay 会被取消)。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/41340.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

移动校园(7)ii:uniapp响应拦截器处理token,以及微信小程序报错当前页面正在处于跳转状态,请稍后再进行跳转....

依据昨天的写完&#xff0c;在token过期之后&#xff0c;再次调用接口&#xff0c;会触发后端拦截&#xff0c;扔进全局错误处理中间件 前端说明提示都没有&#xff0c;只有一个这个&#xff0c;现在优化一下&#xff0c;再写一个类似全局后置守卫&#xff0c;当状态码是401的时…

RAID 冗余磁盘阵列

RAID也是Linux操作系统中管理磁盘的一种方式。 只有Linux操作系统才支持LVM的磁盘管理方式。 而RAID是一种通用的管理磁盘的技术&#xff0c;使用于多种操作系统。 优势&#xff1a;提升数据的读写速度&#xff0c;提升数据的可靠性。具体实现哪什么功能&#xff0c;要看你所…

LVGL移植与VS模拟器使用

一、移植文件介绍 二、移植部分 第一步&#xff1a;创建LVGL文件夹 第二步&#xff1a; 构造LVGL文件夹&#xff1a;LVGL - GUI - lvgl - 第三步&#xff1a;添加文件 3.1 从examples中添加2个.c文件 3.2 从src中添加文件 draw文件 extra文件 第四步&#xff1a; 三、Ke…

Linux系统安装软件包的方法rpm和yum详解

起因&#xff1a; 本篇文章是记录学习Centos7的历程 关于rpm 常见命令 1&#xff09;查看已经安装的软件包 rpm -q 软件包名 2&#xff09;查看文件的相关信息 rpm -qi 软件包名 3&#xff09;查看软件包的依赖关系 就是说要想安装这个软件包&#xff0c;就必须把一些前…

三级_网络技术_04_中小型网络系统总体规划与设计

1.下列关于路由器技术特征的描述中&#xff0c;正确的是()。 吞吐量是指路由器的路由表容量 背板能力决定了路由器的吞吐量 语音、视频业务对延时抖动要求较低 突发处理能力是以最小帧间隔值来衡量的 2.下列关于路由器技术特征的描述中&#xff0c;正确的是()。 路由器的…

springboot公寓租赁系统-计算机毕业设计源码03822

摘要 1 绪论 1.1 研究背景与意义 1.2选题背景 1.3论文结构与章节安排 2 公寓租赁系统系统分析 2.1 可行性分析 2.1.1 技术可行性分析 2.1.2 经济可行性分析 2.1.3 法律可行性分析 2.2 系统功能分析 2.2.1 功能性分析 2.2.2 非功能性分析 2.3 系统用例分析 2.4 系…

韦东山嵌入式linux系列-第一个实验

1 前言 笔者使用的是韦东山STM32MP157 Pro的板子&#xff0c;环境搭建部分按照说明文档配置完成。配置桥接网卡实现板子、windows、ubuntu的通信&#xff0c;也在开发板挂载 Ubuntu 的NFS目录 &#xff0c;这里就不再赘述了。 板子: 192.168.5.9 windows: 192.168.5.10 ubunt…

机械键盘如何挑选

机械键盘的选择是一个关键的决策&#xff0c;因为它直接影响到我们每天的打字体验。在选择机械键盘时&#xff0c;有几个关键因素需要考虑。首先是键盘的键轴类型。常见的键轴类型包括蓝轴、红轴、茶轴和黑轴等。不同的键轴类型具有不同的触发力、触发点和声音。蓝轴通常具有明…

聚类分析方法(一)

目录 一、聚类分析原理&#xff08;一&#xff09;聚类分析概述&#xff08;二&#xff09;聚类的数学定义&#xff08;三&#xff09;簇的常见类型&#xff08;四&#xff09;聚类框架及性能要求&#xff08;五&#xff09;簇的距离 二、划分聚类算法&#xff08;一&#xff0…

Java 有什么必看的书?

Java必看经典书有这两本&#xff1a; 1、Java核心技术速学版&#xff08;第3版&#xff09; 经典Java开发基础书CoreJava速学版本&#xff01;Java入门优选书籍&#xff0c;更新至Java17&#xff0c;内容皆是精华&#xff0c;让Java学习更简单&#xff0c;让Java知识应用更快速…

【Linux】什么是进程间通信?方式有哪些?本质理解?

&#x1f490; &#x1f338; &#x1f337; &#x1f340; &#x1f339; &#x1f33b; &#x1f33a; &#x1f341; &#x1f343; &#x1f342; &#x1f33f; &#x1f344;&#x1f35d; &#x1f35b; &#x1f364; &#x1f4c3;个人主页 &#xff1a;阿然成长日记 …

NoSQL 之 Redis 集群部署

前言&#xff1a; &#xff08;1&#xff09;主从复制&#xff1a;主从复制是高可用Redis的基础&#xff0c;哨兵和集群都是在主从复制基础上实现高可用 的。主从复制主要实现了数据的多机备份&#xff0c;以及对于读操作的负载均衡和简单的故障恢复。缺陷&#xff1a; 故障…

vue3+antd 实现文件夹目录右键菜单功能

原本的目录结构&#xff1a; 右键菜单&#xff1a; 点击菜单以后会触发回调&#xff1a; 完整的前端代码&#xff1a; <template><a-directory-treev-model:expandedKeys"expandedKeys"v-model:selectedKeys"selectedKeys"multipleshow-li…

在 Docker 容器中运行 Vite 开发环境,有这两个问题要注意

容器化开发给我们带来了很多便捷&#xff0c;但是在开发环境下也有一些问题要注意&#xff0c;如果不解决这些问题&#xff0c;你的开发体验不会很好。 容器启动正常&#xff0c;却无法访问 我们用 Docker 启动一个 Vite Vue3 项目的开发环境后&#xff0c;发现端口日志一切…

计算机如何存储浮点数

浮点数组成 在计算机中浮点数通常由三部分组成&#xff1a;符号位、指数位、尾数位。IEEE-754中32位浮点数如下&#xff1a; 上图32bit浮点数包含1bit的符号位&#xff0c;8比特的指数位和23bit的尾数位。对于一个常规浮点数&#xff0c;我们来看看它是如何存储和计算的。这里…

conda env pip install error:No space left on device

conda 环境 pip install error&#xff1a;No space left on device 文章目录 conda 环境 pip install error&#xff1a;No space left on device现象1 实验2 分析和解决办法 现象 非root用户的服务器&#xff0c;需要安装环境&#xff0c;安装的环境超过2GB sudo pip insta…

医疗机器人中的具身智能进展——自主超声策略模型的任务编码和局部探索

医疗机器人一直是具身智能的研究热点。医学图像、医疗触诊、血压血氧、心率脉搏和生物电信号等多模态生物医学信息&#xff0c;不断丰富着医疗机器人的感知范畴。 自主超声 “自主超声”属于具身智能医疗机器人领域中话题度较高的研究方向。作为临床检查的重要手段之一&#…

线性系统理论及应用GUI设计及仿真

目录 1.控制系统的状态空间模型 1.1.状态空间模型 1.2 传递函数模型 1.3 传递函数转换为状态空间模型 1.4.状态空间模型转换为传递函数 1.5.状态空间模型转化为约当标准型 2.线性系统的时域分析 2.1.矩阵指数函数的计算 2.2.线型定常连续系统的状态空间模型求解 3.线…

ubuntu24.04按关键字卸载不需要的apt包

使用的时候发现一个imagemagic无法正常读取文件&#xff0c;试图卸载 man apt经过尝试后&#xff0c;发现list的一个神奇关键字&#xff0c;用来显示已安装的软件包 sudo apt list --installed | grep image按image关键字过滤&#xff1a; 之后按软件名卸载即可 sudo apt pu…

开关电源——调制模式和工作模式

一、开关电源的调制模式 开关电源作为一种广泛应用于电子设备中&#xff0c;用于将一定电压和电流转换为另一种电压和电流的技术&#xff0c;以下是开关电源三种常见的调制模式&#xff1a; 脉冲宽度调制&#xff08;Pulse Width Modulation&#xff09; 脉冲频率调制&#xff…