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.pswp.cn/web/41340.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/41340.shtml
英文地址,請注明出處:http://en.pswp.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…