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;要看你所…

RGB树-美团2023笔试(codefun2000)

题目链接 RGB树-美团2023笔试(codefun2000) 题目内容 塔子哥是一位著名的冒险家&#xff0c;他经常在各种森林里探险。今天&#xff0c;他来到了道成林&#xff0c;这是一片美丽而神秘的森林。在探险途中&#xff0c;他遇到了一棵 n 个节点的树&#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…

【linux】服务器创建RAID1

【linux】服务器创建RAID1 文章目录 【linux】服务器创建RAID1一、配置介绍raid介绍raid类型RAID 0:RAID 1:RAID 5:RAID 6:二、配置RAID硬件RAID:软件RAID:三、软件配置RAID1(以linux为例)1.先进入管理员模式2.安装mdadm工具3.创建raid1数组4.查看RAID数组状态5.格式化和挂载…

机械键盘如何挑选

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

神经网络和安全结合:一种基于神经网络的智能攻击检测与防御系统;构建攻击行为预测模型

目录 神经网络和安全结合 摘要 引言 理论基础 技术实现与创新点 实验验证 结论与展望 一种基于神经网络的智能攻击检测与防御系统 一、系统概述 二、主要功能 三、技术特点 四、应用前景 构建攻击行为预测模型 一、构建攻击行为预测模型的步骤 1. 数据收集 2. …

单链表的学习与基础运用p

当我们在实际做项目&#xff0c;或者是自主开发一点小东西的时候&#xff0c;往往会储存一些数据&#xff0c;有时候我们需要添加这些数据&#xff0c;有时候需要删除&#xff0c;而有时候&#xff0c;仅仅只需要查找到就行。链表中的每一个节点都是一个独立开辟的空间&#xf…

聚类分析方法(一)

目录 一、聚类分析原理&#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;阿然成长日记 …

使用 ChronicleMap 扩展高性能内存缓存

1.扩展内存缓存的挑战 我们用于与各种程序化和需求方平台 (DSP) 集成的应用程序之一是低延迟、高吞吐量的基于 JVM 的应用程序。这是 付款凭单&#xff08;DV&#xff09;付前前验证解决方案的核心组件。自多年前成功推出此解决方案以来&#xff0c;我们不断添加多项关键功能&…

【ChatGPT】全面解析 ChatGPT:从起源到未来

ChatGPT 是由 OpenAI 开发的一个基于 GPT&#xff08;Generative Pre-training Transformer&#xff09;架构的聊天机器人。通过自然语言处理&#xff08;NLP&#xff09;技术&#xff0c;ChatGPT 能够理解和生成语言&#xff0c;与人类进行对话。本文将深入探讨其起源、发展、…

SpringSecurity源码分析-过滤器链是如何植入到spring中的

SpringSecurity源码分析-过滤器链是如何植入到spring中的 一切的源头都是因为在web.xml中配置了这样一个Filter <!--security--><filter><filter-name>springSecurityFilterChain</filter-name><filter-class>org.springframework.web.filter.…

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…