最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C# 異步回調(diào)與等待機(jī)制全解

 更新時(shí)間:2026年04月03日 09:33:41   作者:MutoKazuo  
文章介紹了ManualResetEvent和AutoResetEvent的特性、適用場(chǎng)景以及與TaskCompletionSource和SemaphoreSlim等現(xiàn)代異步方法的對(duì)比,對(duì)C# 異步回調(diào)與等待機(jī)制相關(guān)知識(shí)感興趣的朋友跟隨小編一起看看吧

TaskCompletionSource (TCS)

一個(gè)可以手動(dòng)控制狀態(tài)的 Task。它允許你創(chuàng)建一個(gè)任務(wù),并在稍后的某個(gè)時(shí)間點(diǎn)手動(dòng)將其標(biāo)記為“已完成”。

Task.WhenAny

超時(shí)處理機(jī)制。通過(guò)將“結(jié)果任務(wù)”和“延遲任務(wù)(Task.Delay)”放在一起競(jìng)爭(zhēng),確保程序不會(huì)因?yàn)橄G失而永久死鎖。

private TaskCompletionSource<string> _transTaskSource;
private TaskCompletionSource<bool> _transAviResultSource;
public bool TransResult{get;set;}
public string ImageFileName{get;set;}
private void OnAppMessageReceived(AppMessageEventArgs obj)
{
    if (obj.MessageId == "xxx")
    {
        Dictionary<string, object> dic = JsonHelper.Deserialize<Dictionary<string, object>>(obj.Value);
        if (dic["FileName"].ToString().Contains("avi"))
        {
            bool result = dic["Result"].ToString() == "1";
            _transAviResultSource?.TrySetResult(result);
        }
        else
        {
            string fileName = dic["FileName"].ToString();
            _transTaskSource?.TrySetResult(fileName);
        }
    }
}
protected virtual async Task<string> OnTransStandard(string fileFormat)
{
	switch (fileFormat)
    {
		case "avi":
			_transAviResultSource = new TaskCompletionSource<bool>();
			var result = _transService.Trans2Avi();
			var timeoutTask = Task.Delay(TimeSpan.FromSeconds(15));
			var completedTask = await Task.WhenAny(_transAviResultSource.Task, timeoutTask);
			if (completedTask == _transAviResultSource.Task)
			{
				TransResult = result;
			}
		break;
		default:
			_trasnTaskSource = new TaskCompletionSource<string>();
            _transService.Trans();
            var timeoutTask = Task.Delay(TimeSpan.FromSeconds(30), cancellationToken);
            var completedTask = await Task.WhenAny(_trasnTaskSource.Task, timeoutTask);
            if (completedTask == _trasnTaskSource.Task)
            {
                ImageFileName = await _trasnTaskSource.Task;
            }
            else
            {
                // TODO
            }
		break;
	}
}

優(yōu)化版本

using System.Collections.Concurrent;
// 使用字典支持并發(fā),Key 為唯一標(biāo)識(shí)(如文件名或請(qǐng)求ID)
private readonly ConcurrentDictionary<string, TaskCompletionSource<string>> _transTasks = new();
private readonly ConcurrentDictionary<string, TaskCompletionSource<bool>> _aviTasks = new();
private void OnAppMessageReceived(AppMessageEventArgs obj)
{
    if (obj.MessageId != "xxx") return;
    var dic = JsonHelper.Deserialize<Dictionary<string, object>>(obj.Value);
    string fileName = dic["FileName"]?.ToString() ?? string.Empty;
    if (fileName.Contains("avi"))
    {
        bool result = dic["Result"]?.ToString() == "1";
        // 嘗試從字典中移除并設(shè)置結(jié)果,確保只處理一次
        if (_aviTasks.TryRemove(fileName, out var tcs))
        {
            tcs.TrySetResult(result);
        }
    }
    else
    {
        if (_transTasks.TryRemove(fileName, out var tcs))
        {
            tcs.TrySetResult(fileName);
        }
    }
}
protected virtual async Task<string> OnTransStandard(string fileFormat, string fileName)
{
    // 1. 設(shè)置超時(shí)取消令牌
    using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(fileFormat == "avi" ? 15 : 30));
    try
    {
        if (fileFormat == "avi")
        {
            var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
            _aviTasks[fileName] = tcs;
            _transService.Trans2Avi();
            // 使用 Register 綁定取消令牌到 TCS
            using (cts.Token.Register(() => tcs.TrySetCanceled()))
            {
                TransResult = await tcs.Task;
                return fileName;
            }
        }
        else
        {
            var tcs = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
            _transTasks[fileName] = tcs;
            _transService.Trans();
            using (cts.Token.Register(() => tcs.TrySetCanceled()))
            {
                ImageFileName = await tcs.Task;
                return ImageFileName;
            }
        }
    }
    catch (OperationCanceledException)
    {
        // 統(tǒng)一處理超時(shí)邏輯
        HandleTimeout(fileName, fileFormat);
        return string.Empty;
    }
    finally
    {
        // 確保清理字典,防止內(nèi)存泄漏
        _aviTasks.TryRemove(fileName, out _);
        _transTasks.TryRemove(fileName, out _);
    }
}

同步阻塞式的等待機(jī)制(同步原語(yǔ))

傳統(tǒng)的線程同步對(duì)象 ManualResetEvent 來(lái)強(qiáng)制當(dāng)前線程“停下”執(zhí)行,直到收到特定的信號(hào)。

調(diào)用 OnDataTransformation 的線程會(huì)被物理掛起,不消耗 CPU 周期,但會(huì)占用一個(gè)線程資源,直到 _event.Set() 被調(diào)用。

特性ManualResetEvent (手動(dòng))AutoResetEvent (自動(dòng))
放行數(shù)量調(diào) Set() 后,所有正在等待的線程都會(huì)被放行。調(diào)用 Set() 后,僅有一個(gè)線程會(huì)被放行。
復(fù)位方式必須手動(dòng)調(diào)用 Reset() 才會(huì)再次阻塞線程。只要有一個(gè)線程通過(guò),它會(huì)自動(dòng)回到阻塞狀態(tài)。
典型場(chǎng)景廣播/通知:一個(gè)信號(hào)通知多個(gè)任務(wù)同時(shí)開(kāi)始。同步/排隊(duì):確保對(duì)某個(gè)資源的訪問(wèn)是串行的。
public bool TransResult { get; set; }
public string Filenames { get; set; }
private static readonly ManualResetEvent _event = new ManualResetEvent(false);
private void OnAppMessageReceived(AppMessageEventArgs obj)
{
    if (obj.MessageId == "xxx")
    {
        Dictionary<string, object> dic = JsonHelper.Deserialize<Dictionary<string, object>>(obj.Value);
        if (dic["FileName"].ToString().Contains("avi"))
        {
            TransResult = dic["Result"].ToString() == "1";
            _event.Set();
        }
        else
        {
            Filenames = dic["FileName"].ToString();
            _event.Set();
        }
    }
}
public virtual void OnDataTransformation()
{
	cancellationToken.ThrowIfCancellationRequested();
	_TranService.TransImage();
    _event.Reset();
    _event.WaitOne();
	cancellationToken.ThrowIfCancellationRequested();
}

選擇 ManualResetEvent 還是 AutoResetEvent 取決于你的控制目標(biāo):是想給“一群人”發(fā)信號(hào),還是想讓“一個(gè)人”過(guò)閘機(jī)。

1. ManualResetEvent(手動(dòng)重置)

適用場(chǎng)景:狀態(tài)通知 / 廣播 (Broadcasting)

它通常用于表示一個(gè)“開(kāi)關(guān)”或“階段完成”的狀態(tài)。一旦這個(gè)狀態(tài)達(dá)成了,所有依賴(lài)它的線程都可以繼續(xù)。

典型例子:系統(tǒng)初始化

  • 你的主程序啟動(dòng)時(shí)有多個(gè)后臺(tái)服務(wù)(數(shù)據(jù)庫(kù)連接、緩存加載、配置文件讀?。?ul>
  • 主線程調(diào)用 _initEvent.WaitOne()。
  • 只有當(dāng)所有初始化工作全部完成后,才調(diào)用一次 Set()。
  • 此時(shí),所有卡在 WaitOne 處的邏輯都會(huì)同時(shí)被釋放。

典型例子:暫停/恢復(fù)功能

  • 在下載器或播放器中,按下“暫停”即 Reset()(關(guān)閘),所有下載線程 WaitOne;按下“開(kāi)始”即 Set()(開(kāi)閘),所有線程同時(shí)恢復(fù)工作。

2. AutoResetEvent(自動(dòng)重置)

適用場(chǎng)景:獨(dú)占資源 / 生產(chǎn)者-消費(fèi)者 (Queueing)

它通常用于確保任務(wù)的串行化執(zhí)行,或者作為簡(jiǎn)單的線程間信號(hào)傳遞。

典型例子:任務(wù)隊(duì)列(單線程處理)

  • 你有一個(gè)后臺(tái)線程專(zhuān)門(mén)處理發(fā)郵件的操作。
    • 當(dāng)有新郵件進(jìn)入隊(duì)列時(shí),調(diào)用一次 Set()。
    • 后臺(tái)處理線程 WaitOne() 收到信號(hào),起來(lái)發(fā)一封郵件。
    • 發(fā)完后,由于是 AutoReset,它會(huì)自動(dòng)回到阻塞狀態(tài),等待下一次 Set()。

典型例子:由于硬件限制的互斥訪問(wèn)

  • 像你代碼中這種“發(fā)送指令 -> 等待硬件回傳”的模式。如果你想確保發(fā)一個(gè)收一個(gè),且不希望第二個(gè)指令在第一個(gè)指令沒(méi)返回前就跑掉,AutoResetEvent 更安全,因?yàn)樗幚硗暌淮螘?huì)自動(dòng)“關(guān)門(mén)”。

3. 對(duì)比

維度ManualResetEventAutoResetEvent
形象比喻大門(mén)的閘刀開(kāi)關(guān):拉上去,所有人都能進(jìn);拉下來(lái),所有人都得停。地鐵的旋轉(zhuǎn)閘機(jī):刷一次卡(Set),只能進(jìn)去一個(gè)人,進(jìn)去后閘機(jī)立刻鎖死。
核心邏輯狀態(tài)驅(qū)動(dòng):側(cè)重于“某個(gè)條件是否達(dá)成”。事件驅(qū)動(dòng):側(cè)重于“某個(gè)動(dòng)作是否發(fā)生”。
重置時(shí)機(jī)你認(rèn)為這個(gè)狀態(tài)不再有效時(shí)(手動(dòng))。線程穿過(guò) WaitOne 的那一刻(自動(dòng))。

4. 為什么用得少了?

在高性能開(kāi)發(fā)中,這兩個(gè)類(lèi)正逐漸被以下方案取代:

  1. TaskCompletionSource (TCS)
    1. 理由:它是異步非阻塞的(Async/Non-blocking)。前面的 Manual/Auto 都會(huì)死死占住一個(gè)操作系統(tǒng)線程,非常浪費(fèi)資源。
  2. SemaphoreSlim (信號(hào)量)
    1. 理由:它比 AutoResetEvent 更強(qiáng)大。它支持 WaitAsync(異步等待),而且可以控制允許 N 個(gè)線程同時(shí)通過(guò),而不僅僅是一個(gè)。
  3. ManualResetEventSlim
    1. 理由:如果你非要用同步等待,請(qǐng)優(yōu)先使用帶 Slim 后綴的版本。它在等待時(shí)間很短時(shí)會(huì)先進(jìn)行“自旋(Spin)”,不直接切換到昂貴的內(nèi)核模式,性能更好。

異步等待 (Await) vs 同步掛起 (Wait)

維度TaskCompletionSourceManual/AutoResetEvent
編程模型異步 (Asynchronous)?;?Task,符合現(xiàn)代 .NET 開(kāi)發(fā)習(xí)慣。同步 (Synchronous)。基于操作系統(tǒng)的內(nèi)核對(duì)象。
線程利用率。await 時(shí)線程會(huì)釋放回線程池,去處理其他任務(wù)。。當(dāng)前線程被徹底卡死(Block),什么都干不了。
超時(shí)處理靈活。通過(guò) Task.Delay 或 CancellationToken 輕松實(shí)現(xiàn)。較硬。需給 WaitOne(timeout) 傳參,且寫(xiě)法略顯臃腫。
UI 響應(yīng)性友好。如果在 UI 線程調(diào)用,界面不會(huì)卡死。危險(xiǎn)。如果在 UI 線程調(diào)用,界面會(huì)直接崩潰/無(wú)響應(yīng)(Deadlock)。
并發(fā)支持較好。通過(guò) tcs 實(shí)例可以區(qū)分不同的請(qǐng)求。。static 的 _event 意味著全局只能同時(shí)處理一個(gè)任務(wù)。

第一種 (TaskCompletionSource) —— 手機(jī)短信提醒: 你該干嘛干嘛(去洗澡、打游戲),線程被釋放回去了??爝f到了,手機(jī)響了(Task 完成),你再回到門(mén)口處理快遞。這種是非阻塞的。

第二種 (ManualResetEvent) —— 站在門(mén)口死等: 你推掉了一切活動(dòng),就站在門(mén)口盯著路口(線程被掛起)??爝f員沒(méi)來(lái),你哪也不去,也不說(shuō)話(huà)??爝f員一招手(Set),你立刻動(dòng)起來(lái)。這種是同步阻塞的。

同步方式 (EventWaitHandle)

// 這是一條死胡同,除非有人開(kāi)門(mén),否則車(chē)(線程)就停死在這里
_event.WaitOne();
// 只有開(kāi)門(mén)后,才能跑這一行
DoNextStep();
  • 后果:如果你在 UI 線程(如點(diǎn)擊按鈕)里這么寫(xiě),你的軟件界面會(huì)直接“未響應(yīng)”,因?yàn)?UI 線程被阻塞了,沒(méi)法處理鼠標(biāo)點(diǎn)擊和界面刷新。

異步方式 (TaskCompletionSource)

// 這是一條路口,車(chē)(線程)發(fā)現(xiàn)紅燈,就先掉頭去干別的活了
await _tcs.Task;
// 綠燈亮了(SetResult),會(huì)有另一輛車(chē)(或原車(chē))回來(lái)繼續(xù)跑
DoNextStep();
  • 后果:UI 依然絲滑。await 釋放了當(dāng)前線程,讓它回消息循環(huán)里去處理界面繪制,等結(jié)果到了再回來(lái)。

.NET中的位置

在 .NET 專(zhuān)門(mén)的同步分類(lèi)中,它們屬于內(nèi)核模式同步對(duì)象(Kernel-mode objects)。

類(lèi)別代表組件特點(diǎn)
內(nèi)核模式 (同步)ManualResetEvent, AutoResetEvent, Mutex重型。涉及操作系統(tǒng)內(nèi)核切換,跨進(jìn)程可用,但性能開(kāi)銷(xiāo)大。
混合模式 (同步)ManualResetEventSlim, SemaphoreSlim輕量。先自旋再掛起,性能極高,是現(xiàn)代同步的首選。
異步模式TaskCompletionSource, Task.WhenAny現(xiàn)代。完全不阻塞線程,支撐高并發(fā)的核心。

到此這篇關(guān)于C# 異步回調(diào)與等待機(jī)制全解的文章就介紹到這了,更多相關(guān)C# 異步回調(diào)與等待機(jī)制內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C#實(shí)現(xiàn)的SN快速輸入工具實(shí)例

    C#實(shí)現(xiàn)的SN快速輸入工具實(shí)例

    這篇文章主要介紹了C#實(shí)現(xiàn)的SN快速輸入工具,以實(shí)例的形式詳細(xì)講述了C#實(shí)現(xiàn)序列號(hào)快速輸入的方法,是非常實(shí)用的技巧,需要的朋友可以參考下
    2014-11-11
  • C#使用Oracle.ManagedDataAccess.dll組件連接Oracle數(shù)據(jù)庫(kù)

    C#使用Oracle.ManagedDataAccess.dll組件連接Oracle數(shù)據(jù)庫(kù)

    這篇文章介紹了C#使用Oracle.ManagedDataAccess.dll組件連接Oracle數(shù)據(jù)庫(kù)的方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-05-05
  • .NET WinForm實(shí)現(xiàn)在listview中添加progressbar的方法

    .NET WinForm實(shí)現(xiàn)在listview中添加progressbar的方法

    這篇文章主要介紹了.NET WinForm實(shí)現(xiàn)在listview中添加progressbar的方法,結(jié)合實(shí)例形式簡(jiǎn)單分析了進(jìn)度條控件的添加與使用方法,需要的朋友可以參考下
    2017-05-05
  • 一文詳解C#訪問(wèn)修飾符的終極指南

    一文詳解C#訪問(wèn)修飾符的終極指南

    這篇文章主要為大家詳細(xì)介紹了C#中訪問(wèn)修飾符的相關(guān)使用方法,包括public,private,protected,等,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解下
    2026-03-03
  • C#日歷樣式的下拉式計(jì)算器實(shí)例講解

    C#日歷樣式的下拉式計(jì)算器實(shí)例講解

    如果我們正在做一個(gè)類(lèi)似于庫(kù)存控制和計(jì)費(fèi)系統(tǒng)的項(xiàng)目,有些部分可能必須手動(dòng)計(jì)算數(shù)值。因此,用戶(hù)就不得不使用計(jì)算器得到結(jié)果,再填入到輸入字段中,或者在工作窗口上單獨(dú)打開(kāi)一個(gè)計(jì)算器窗口??傊鞣N不便和麻煩。
    2015-09-09
  • C#RSA對(duì)接JAVA中RSA方式代碼實(shí)例

    C#RSA對(duì)接JAVA中RSA方式代碼實(shí)例

    在本篇文章中小編給各位整理的是關(guān)于C#RSA對(duì)接JAVA中RSA方式代碼實(shí)例,有需要的朋友們可以參考一下。
    2019-09-09
  • WPF實(shí)現(xiàn)自定義Panel面板的示例詳解

    WPF實(shí)現(xiàn)自定義Panel面板的示例詳解

    WPF中的Panel(面板),是繼承自FrameworkElement的抽象類(lèi),表示一個(gè)可以用來(lái)排列子元素的面板,本文主要來(lái)和大家聊聊WPF如何實(shí)現(xiàn)自定義Panel,感興趣的可以了解下
    2023-09-09
  • C#實(shí)現(xiàn)鬧鐘AlarmClock實(shí)例代碼

    C#實(shí)現(xiàn)鬧鐘AlarmClock實(shí)例代碼

    這篇文章主要介紹了C#實(shí)現(xiàn)鬧鐘AlarmClock實(shí)例代碼,很實(shí)用的功能,需要的朋友可以參考下
    2014-08-08
  • Silverlight實(shí)現(xiàn)星星閃爍動(dòng)畫(huà)

    Silverlight實(shí)現(xiàn)星星閃爍動(dòng)畫(huà)

    這篇文章主要為大家詳細(xì)介紹了Silverlight實(shí)現(xiàn)星星閃爍動(dòng)畫(huà),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2018-07-07
  • C# Color.FromArgb()及系統(tǒng)顏色對(duì)照表一覽

    C# Color.FromArgb()及系統(tǒng)顏色對(duì)照表一覽

    這篇文章主要介紹了C# Color.FromArgb()及系統(tǒng)顏色對(duì)照表一覽,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2021-01-01

最新評(píng)論

惠来县| 扎兰屯市| 鹿泉市| 潜江市| 息烽县| 册亨县| 阿图什市| 财经| 碌曲县| 阳城县| 温泉县| 习水县| 鄢陵县| 甘孜县| 吉安县| 司法| 曲靖市| 平舆县| 宁晋县| 金沙县| 鸡东县| 青海省| 龙泉市| 同心县| 渭南市| 襄樊市| 庆元县| 竹溪县| 宝兴县| 沛县| 西峡县| 准格尔旗| 衡山县| 中超| 龙口市| 增城市| 锦屏县| 香格里拉县| 高雄县| 虹口区| 清徐县|