主頁 > 軟體工程 > 在熱路徑上用異步API包裝基于回呼的API時,避免分配并保持并發性

在熱路徑上用異步API包裝基于回呼的API時,避免分配并保持并發性

2021-10-16 09:39:04 軟體工程

我在 StackOverflow 上閱讀了許多關于使用 Task 來包裝基于回呼的 API 的文章和問題,我正試圖在與 Solace PubSub 訊息代理進行通信時使用這種技術。

我最初的觀察是,這種技術似乎是在轉移并發性的責任。例如,Solace代理庫有一個Send()方法,它可能會阻塞,然后我們在網路通信完成后得到一個回呼以指示 "真正的 "成功或失敗。因此,這個Send()方法可以被快速呼叫,而供應商庫在內部限制了并發性。

當你在周圍放置一個任務時,你似乎要么將操作序列化(foreach message await SendWrapperAsync(message)),要么通過決定啟動多少個任務(例如,使用 TPL 資料流)來自己接管并發性的責任。

在任何情況下,我決定用一個擔保人來包裝Send呼叫,該擔保人將永遠重試,直到回呼顯示成功為止,并對并發性負責。這是一個 "有保障 "的訊息傳遞系統。失敗不是一種選擇。這需要保證人能夠應用背壓,但這其實不在這個問題的范圍內。我在下面的示例代碼中對它有一些評論。

它確實意味著我的熱路徑,即包裹了發送 回呼的路徑,由于重試邏輯而變得 "特別熱"。因此,這里有大量的 TaskCompletionSource 創建。

供應商自己的檔案建議盡可能地重用他們的Message物件,而不是為每個Send重新創建它們。我已經決定使用Channel作為一個環形緩沖器。但這讓我想知道--除了TaskCompletionSource方法之外,是否還有其他的替代方法--也許有一些其他的物件也可以被快取在環形緩沖區中并被重用,從而達到相同的結果?

我意識到這可能是對微觀優化的過度熱心嘗試,而且說實話,我正在探索C#的幾個方面,這些方面高于我的工資等級(我是一個SQL人,真的),所以我可能錯過了一些明顯的東西。如果答案是 "你實際上不需要這種優化",那就不能讓我安心了。如果答案是 "這確實是唯一明智的方法",我的好奇心就會得到滿足。

這里是一個功能齊全的控制臺應用程式,它模擬了MockBroker物件中的Solace庫的行為,以及我對它進行封裝的嘗試。我的熱點路徑是Guarantor類中的SendOneAsync方法。這段代碼對SO來說可能有點太長了,但這是我能創建的最小的演示,它捕捉到了所有的重要元素。

using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Channels;
using System.Threading.Tasks;

內部 class Message { public bool sent。public int payload; public object correlator; }

//模擬第三方庫行為internal class MockBroker
{
    public bool TrySendMessage m, Action< Message> callback
    {
        if (r.NextDouble() < 0.5) return false; //模擬立即失敗的機會/"會阻塞 "的反應。
        Task.Run(() => { Thread.Sleep(100); m.send = r.NextDouble() < 0.5; callback(m); }); //模擬網路呼叫return true。
    }

    private Random r = new();
}

//將MockBroker變成一個具有異步并發限制的 "保證 "發送器internal class Guarantorpublic Guarantor(int maxConcurrency)
    {
        _broker = new MockBroker()。
        //避免SendOneAsync中的訊息分配。
        _ringBuffer = Channel.CreateBounded<Message>(maxConcurrency)。
        for (int i = 0; i < maxConcurrency; i  ) _ringBuffer.Writer.TryWrite(new Message())。
    }

    //真正的代碼推送到T.T.T.DataFlow塊中,具有有界容量和并行性//執行選項都等于這里的maxConcurrency,提供并發性和背壓public async Task Post(int payload) => await SendOneAsync(payload);

    private async Task SendOneAsync(int payload)。
    {
        Message msg = await _ringBuffer.Reader.ReadAsync()。
        msg.payload = payload;
        //發送必須最終成功while (true)
        {
            // ***能否避免這種分配?***
            var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously)。
            msg.correlator = tcs;
            //真實代碼中的類方法,在此行內以使邏輯更明顯。
            Action<Message> callback = (msg) => (msg.correlator as TaskCompletionSource<bool>).SetResult(msg.send)。
            if (_broker.TrySend(msg, callback) && await tcs.Task) break;
            else
            {
                //簡單的演示重試邏輯。
                Console.WriteLine($"retrying {msg.payload}")。
                await Task.Delay(500) 。
            }
        }
        //真正的代碼在這里引發一個事件以表示成功交付await _ringBuffer.Writer.WriteAsync(msg)。
        Console.WriteLine(payload)。
    }

    private Channel<Message> _ringBuffer;
    private MockBroker _broker;
}

internal class Programprivate static async 任務 Main(string[] args) 
    {
        //最多 10 個并發的發送。
        保證人g = new(10)。
        //hacky simulation,因為在這個演示中,沒有任何東西產生連續事件,//沒有DataFlowBlock提供并發性(它將被通道所限制),//沒有人在訊息成功發送時進行通知。
        List<Task> sends = new100)。
        for (int i = 0; i < 100; i  ) sends.Add(g.Post(i))。
        await Task.WhenAll(sends)。
    }

uj5u.com熱心網友回復:

是的,你可以避免分配TaskCompletionSource實體,通過使用輕量級的ValueTasks代替Tasks。起初,你需要一個可重用的物件來實作IValueTaskSource<T>介面,而Message似乎是完美的候選。為了實作這個介面,你可以使用ManualResetValueTaskSourceCore<T>結構。這是一個可變的結構,所以它不應該被宣告為只讀。你只需要將介面方法委托給這個名字很長的結構的相應方法即可:

using System.Threading.Tasks.Sources; internal class Message : IValueTaskSource<bool> { public bool sent; public int payload; public object correlator; private ManualResetValueTaskSourceCore<bool> _source; // Mutable struct, not readonlypublic void Reset() => _source.Reset()。 public short Version => _source.Version; public void SetResult(bool result) => _source. SetResult(result)。 ValueTaskSourceStatus IValueTaskSource<bool>.GetStatus(short token) => _source.GetStatus(token); void IValueTaskSource<bool>.OnCompleted(Action<object> continuation, object state, short token, ValueTaskSourceOnCompletedFlags flags) => _source.OnCompleted(continuation, state, token, flags)。 bool IValueTaskSource<bool>.GetResult(short token) => _source.GetResult(token)。 }

三個成員GetStatusOnCompletedGetResult是實作該介面的必要條件。其他三個成員(ResetVersionSetResult)將被用于創建和控制ValueTask<bool>s。

現在讓我們把MockBroker類的TrySend方法包裝成一個異步方法TrySendAsync,它回傳一個ValueTask<bool>

static class MockBrokerExtensions
{
    public staticValueTask<bool> TrySendAsync(this MockBroker source, message)。
    {
        message.Reset()。
        bool result = source.TrySend(message, m => m.SetResult(m.sed))。
        if (!result) message.SetResult(false)。
        return new ValueTask<bool>(message, message.Version)。
    }

message.Reset();重置了IValueTaskSource<bool>,并宣告之前的異步操作已經完成。一個IValueTaskSource<T>一次只支持一個異步操作,產生的ValueTask<T>只能被等待一次,而且在下一個Reset()之后就不能再被等待了。這就是你為避免分配物件所必須付出的代價:你必須遵循更嚴格的規則。如果你試圖彎曲規則(有意或無意),ManualResetValueTaskSourceCore<T>將開始到處拋出InvalidOperationExceptions。

現在讓我們使用TrySendAsync擴展方法:

while (true)
{
    if (await _broker.TrySendAsync(msg)) break;

    //簡單演示重試邏輯
    Console.WriteLine($"retrying {msg.payload}")。
    await Task.Delay(500) 。

你可以在Console中列印整個操作前后的GC.GetTotalAllocatedBytes(true),以看到區別。請確保在Release模式下運行應用程式,以看到真實的畫面。你可能會發現差別并不明顯,因為一個TaskCompletionSource實體的大小與Task.Delay所分配的位元組,以及為在Console中寫東西所產生的所有string相比,是相當小的。

轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/318036.html

標籤:

上一篇:包裹在標簽中時無法選擇Razor單選按鈕

下一篇:在Node中使用異步迭代器進行批量請求和并行處理

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • Git本地庫既關聯GitHub又關聯Gitee

    創建代碼倉庫 使用gitee舉例(github和gitee差不多) 1.在gitee右上角點擊+,選擇新建倉庫 ? 2.選擇填寫倉庫資訊,然后進行創建 ? 3.服務端已經準備好了,本地開始作準備 (1)Git 全域設定 git config --global user.name "成鈺" git c ......

    uj5u.com 2020-09-10 05:04:14 more
  • CODING DevOps 代碼質量實戰系列第二課,相約周三

    隨著 ToB(企業服務)的興起和 ToC(消費互聯網)產品進入成熟期,線上故障帶來的損失越來越大,代碼質量越來越重要,而「質量內建」正是 DevOps 核心理念之一。**《DevOps 代碼質量實戰(PHP 版)》**為 CODING DevOps 代碼質量實戰系列的第二課,同時也是本系列的 PHP ......

    uj5u.com 2020-09-10 05:07:43 more
  • 推薦Scrum書籍

    推薦Scrum書籍 直接上干貨,推薦書籍清單如下(推薦有順序的哦) Scrum指南 Scrum精髓 Scrum敏捷軟體開發 Scrum捷徑 硝煙中的Scrum和XP : 我們如何實施Scrum 敏捷軟體開發:Scrum實戰指南 Scrum要素 大規模Scrum:大規模敏捷組織的設計 用戶故事地圖 用 ......

    uj5u.com 2020-09-10 05:07:45 more
  • CODING DevOps 代碼質量實戰系列最后一課,周四發車

    隨著 ToB(企業服務)的興起和 ToC(消費互聯網)產品進入成熟期,線上故障帶來的損失越來越大,代碼質量越來越重要,而「質量內建」正是 DevOps 核心理念之一。 **《DevOps 代碼質量實戰(Java 版)》**為 CODING DevOps 代碼質量實戰系列的最后一課,同時也是本系列的 ......

    uj5u.com 2020-09-10 05:07:52 more
  • 敏捷軟體工程實踐書籍

    Scrum轉型想要做好,第一步先了解并真正落實Scrum,那么我推薦的Scrum書籍是要看懂并實踐的。第二步是團隊的工程實踐要做扎實。 下面推薦工程實踐書單: 重構:改善既有代碼的設計 決議極限編程 : 擁抱變化 代碼整潔代碼 程式員的職業素養 修改代碼的藝術 撰寫可讀代碼的藝術 測驗驅動開發 : ......

    uj5u.com 2020-09-10 05:07:55 more
  • Jenkins+svn+nginx實作windows環境自動部署vue前端專案

    前面文章介紹了Jenkins+svn+tomcat實作自動化部署,現在終于有空抽時間出來寫下Jenkins+svn+nginx實作自動部署vue前端專案。 jenkins的安裝和配置已經在前面文章進行介紹,下面介紹實作vue前端專案需要進行的哪些額外的步驟。 注意:在安裝jenkins和nginx的 ......

    uj5u.com 2020-09-10 05:08:49 more
  • CODING DevOps 微服務專案實戰系列第一課,明天等你

    CODING DevOps 微服務專案實戰系列第一課**《DevOps 微服務專案實戰:DevOps 初體驗》**將由 CODING DevOps 開發工程師 王寬老師 向大家介紹 DevOps 的基本理念,并探討為什么現代開發活動需要 DevOps,同時將以 eShopOnContainers 項 ......

    uj5u.com 2020-09-10 05:09:14 more
  • CODING DevOps 微服務專案實戰系列第二課來啦!

    近年來,工程專案的結構越來越復雜,需要接入合適的持續集成流水線形式,才能滿足更多變的需求,那么如何優雅地使用 CI 能力提升生產效率呢?CODING DevOps 微服務專案實戰系列第二課 《DevOps 微服務專案實戰:CI 進階用法》 將由 CODING DevOps 全堆疊工程師 何晨哲老師 向 ......

    uj5u.com 2020-09-10 05:09:33 more
  • CODING DevOps 微服務專案實戰系列最后一課,周四開講!

    隨著軟體工程越來越復雜化,如何在 Kubernetes 集群進行灰度發布成為了生產部署的”必修課“,而如何實作安全可控、自動化的灰度發布也成為了持續部署重點關注的問題。CODING DevOps 微服務專案實戰系列最后一課:**《DevOps 微服務專案實戰:基于 Nginx-ingress 的自動 ......

    uj5u.com 2020-09-10 05:10:00 more
  • CODING 儀表盤功能正式推出,實作作業資料可視化!

    CODING 儀表盤功能現已正式推出!該功能旨在用一張張統計卡片的形式,統計并展示使用 CODING 中所產生的資料。這意味著無需額外的設定,就可以收集歸納寶貴的作業資料并予之量化分析。這些海量的資料皆會以圖表或串列的方式躍然紙上,方便團隊成員隨時查看各專案的進度、狀態和指標,云端協作迎來真正意義上 ......

    uj5u.com 2020-09-10 05:11:01 more
最新发布
  • windows系統git使用ssh方式和gitee/github進行同步

    使用git來clone專案有兩種方式:HTTPS和SSH:
    HTTPS:不管是誰,拿到url隨便clone,但是在push的時候需要驗證用戶名和密碼;
    SSH:clone的專案你必須是擁有者或者管理員,而且需要在clone前添加SSH Key。SSH 在push的時候,是不需要輸入用戶名的,如果配置... ......

    uj5u.com 2023-04-19 08:41:12 more
  • windows系統git使用ssh方式和gitee/github進行同步

    使用git來clone專案有兩種方式:HTTPS和SSH:
    HTTPS:不管是誰,拿到url隨便clone,但是在push的時候需要驗證用戶名和密碼;
    SSH:clone的專案你必須是擁有者或者管理員,而且需要在clone前添加SSH Key。SSH 在push的時候,是不需要輸入用戶名的,如果配置... ......

    uj5u.com 2023-04-19 08:35:34 more
  • 2023年農牧行業6大CRM系統、5大場景盤點

    在物聯網、大資料、云計算、人工智能、自動化技術等現代資訊技術蓬勃發展與逐步成熟的背景下,數字化正成為農牧行業供給側結構性變革與高質量發展的核心驅動因素。因此,改造和提升傳統農牧業、開拓創新現代智慧農牧業,加快推進農牧業的現代化、資訊化、數字化建設已成為農牧業發展的重要方向。 當下,企業數字化轉型已經 ......

    uj5u.com 2023-04-18 08:05:44 more
  • 2023年農牧行業6大CRM系統、5大場景盤點

    在物聯網、大資料、云計算、人工智能、自動化技術等現代資訊技術蓬勃發展與逐步成熟的背景下,數字化正成為農牧行業供給側結構性變革與高質量發展的核心驅動因素。因此,改造和提升傳統農牧業、開拓創新現代智慧農牧業,加快推進農牧業的現代化、資訊化、數字化建設已成為農牧業發展的重要方向。 當下,企業數字化轉型已經 ......

    uj5u.com 2023-04-18 08:00:18 more
  • 計算機組成原理—存盤器

    計算機組成原理—硬體結構 二、存盤器 1.概述 存盤器是計算機系統中的記憶設備,用來存放程式和資料 1.1存盤器的層次結構 快取-主存層次主要解決CPU和主存速度不匹配的問題,速度接近快取 主存-輔存層次主要解決存盤系統的容量問題,容量接近與價位接近于主存 2.主存盤器 2.1概述 主存與CPU的聯 ......

    uj5u.com 2023-04-17 08:20:31 more
  • 談一談我對協同開發的一些認識

    如今各互聯網公司普通都使用敏捷開發,采用小步快跑的形式來進行專案開發。如果是小專案或者小需求,那一個開發可能就搞定了。但對于電商等復雜的系統,其功能多,結構復雜,一個人肯定是搞不定的,所以都是很多人來共同開發維護。以我曾經待過的商城團隊為例,光是后端開發就有七十多人。 為了更好地開發這類大型系統,往 ......

    uj5u.com 2023-04-17 08:18:55 more
  • 專案管理PRINCE2核心知識點整理

    PRINCE2,即 PRoject IN Controlled Environment(受控環境中的專案)是一種結構化的專案管理方法論,由英國政府內閣商務部(OGC)推出,是英國專案管理標準。
    PRINCE2 作為一種開放的方法論,是一套結構化的專案管理流程,描述了如何以一種邏輯性的、有組織的方法,... ......

    uj5u.com 2023-04-17 08:18:51 more
  • 談一談我對協同開發的一些認識

    如今各互聯網公司普通都使用敏捷開發,采用小步快跑的形式來進行專案開發。如果是小專案或者小需求,那一個開發可能就搞定了。但對于電商等復雜的系統,其功能多,結構復雜,一個人肯定是搞不定的,所以都是很多人來共同開發維護。以我曾經待過的商城團隊為例,光是后端開發就有七十多人。 為了更好地開發這類大型系統,往 ......

    uj5u.com 2023-04-17 08:18:00 more
  • 專案管理PRINCE2核心知識點整理

    PRINCE2,即 PRoject IN Controlled Environment(受控環境中的專案)是一種結構化的專案管理方法論,由英國政府內閣商務部(OGC)推出,是英國專案管理標準。
    PRINCE2 作為一種開放的方法論,是一套結構化的專案管理流程,描述了如何以一種邏輯性的、有組織的方法,... ......

    uj5u.com 2023-04-17 08:17:55 more
  • 計算機組成原理—存盤器

    計算機組成原理—硬體結構 二、存盤器 1.概述 存盤器是計算機系統中的記憶設備,用來存放程式和資料 1.1存盤器的層次結構 快取-主存層次主要解決CPU和主存速度不匹配的問題,速度接近快取 主存-輔存層次主要解決存盤系統的容量問題,容量接近與價位接近于主存 2.主存盤器 2.1概述 主存與CPU的聯 ......

    uj5u.com 2023-04-17 08:12:06 more