女人久久久,最近更新中文字幕在线,成人国内精品久久久久影院vr,中文字幕亚洲综合久久综合,久久精品秘?一区二区三区美小说

原創(chuàng)生活

國內(nèi) 商業(yè) 滾動(dòng)

基金 金融 股票

期貨金融

科技 行業(yè) 房產(chǎn)

銀行 公司 消費(fèi)

生活滾動(dòng)

保險(xiǎn) 海外 觀察

財(cái)經(jīng) 生活 期貨

當(dāng)前位置:行業(yè) >

全球看點(diǎn):ET框架6.0分析二、異步編程

文章來源:博客園  發(fā)布時(shí)間: 2023-05-15 09:20:32  責(zé)任編輯:cfenews.com
+|-
概述

ET框架很多地方都用到了異步,例如資源加載、AI、Actor模型等等。ET框架對C#的異步操作進(jìn)行了一定程度的封裝和改造,有一些特點(diǎn):

顯式的或者說強(qiáng)調(diào)了使用C#異步實(shí)現(xiàn)協(xié)程機(jī)制(其實(shí)C#的異步編程天生就能實(shí)現(xiàn)這種用法)強(qiáng)制單線程異步?jīng)]有使用C#庫的Task,自己實(shí)現(xiàn)了ETTask等類實(shí)現(xiàn)了協(xié)程鎖

為了更好的理解下面的內(nèi)容,推薦先看一下之前寫的這兩篇文章:

關(guān)于異步對CallbackHell的優(yōu)化 跳轉(zhuǎn)鏈接:《Lua CallbackHell優(yōu)化》關(guān)于C#異步編程介紹和底層實(shí)現(xiàn)(最好看下,不然下面有些內(nèi)容不太好理解) 跳轉(zhuǎn)鏈接:《C# 異步編程async/await》ETTask

C# 的異步函數(shù)有三個(gè)返回值(現(xiàn)在好像.NET7又多了一個(gè)ValueTask):Task,Task,void,對應(yīng)的,ET框架也一樣對應(yīng)實(shí)現(xiàn)了:ETTask,ETTask/,ETVoid,其實(shí)現(xiàn)相比C#簡化了一些邏輯,并添加一些新的特性以適應(yīng)ET框架,其實(shí)使用起來是差不多的。為了實(shí)現(xiàn)ETTask,也實(shí)現(xiàn)了對應(yīng)AsyncTaskCompletedMethodBuilder的AsyncETTaskCompletedMethodBuilder等類(其實(shí)還C#原來的邏輯差不太多,有興趣可以看下上述C# 異步編程的鏈接)。


【資料圖】

ETTask添加了一些特性:

支持對象池顯式強(qiáng)調(diào)協(xié)程
[DebuggerHidden]private async ETVoid InnerCoroutine(){    await this;}[DebuggerHidden]public void Coroutine(){    InnerCoroutine().Coroutine();}

可以看到這里的所謂協(xié)程Coroutine,其實(shí)等效于 await task,只是平平無奇的異步調(diào)用罷了

異常消息打印同步上線文 SynchronizationContext

C#異步編程在大多數(shù)情況下會(huì)使用多線程,ET的異步操作例如定時(shí)器等,使用多線程的開銷相比較大,且ET框架是多進(jìn)程,性能是分?jǐn)偟蕉鄠€(gè)進(jìn)程中。所以ET使用了單線程的異步。

ThreadSynchronizationContext繼承自SynchronizationContext,在構(gòu)造初始化是會(huì)把自身設(shè)為當(dāng)前SynchronizationContext.Current,重寫了Post(異步消息分派到同步上下文)方法,來改寫異步消息的分派到當(dāng)前線程(就是進(jìn)入隊(duì)列)。

而異步函數(shù)在執(zhí)行時(shí),會(huì)獲取當(dāng)前上下文(__builder.AwaitUnsafeOnCompleted方法會(huì)調(diào)用GetCompletionAction,內(nèi)部調(diào)用ExecutionContext.FastCapture(),這個(gè)方法內(nèi)部捕獲SynchronizationContext,感興趣可以關(guān)鍵詞搜索下)

public class ThreadSynchronizationContext : SynchronizationContext{    // 線程同步隊(duì)列,發(fā)送接收socket回調(diào)都放到該隊(duì)列,由poll線程統(tǒng)一執(zhí)行    private readonly ConcurrentQueue queue = new ConcurrentQueue();    private Action a;    public void Update()    {        while (true)        {            if (!this.queue.TryDequeue(out a))            {                return;            }            try            {                a();            }            catch (Exception e)            {                Log.Error(e);            }        }    }    public override void Post(SendOrPostCallback callback, object state)    {        this.Post(() => callback(state));    }        public void Post(Action action)    {        this.queue.Enqueue(action);    }}public class MainThreadSynchronizationContext: Singleton, ISingletonUpdate{    private readonly ThreadSynchronizationContext threadSynchronizationContext = new ThreadSynchronizationContext();    public MainThreadSynchronizationContext()    {        SynchronizationContext.SetSynchronizationContext(this.threadSynchronizationContext);    }        public void Update()    {        this.threadSynchronizationContext.Update();    }        public void Post(SendOrPostCallback callback, object state)    {        this.Post(() => callback(state));    }        public void Post(Action action)    {        this.threadSynchronizationContext.Post(action);    }}// MainThreadSynchronizationContext.Instance.Update()Game.Update();

ThreadSynchronizationContex由包裹的MainThreadSynchronizationContext驅(qū)動(dòng)更新,MainThreadSynchronizationContext是個(gè)單件,由外面驅(qū)動(dòng)。更新Update方法會(huì)把隊(duì)列里的委托取出執(zhí)行。

SynchronizationContext

假設(shè)有兩個(gè)線程,一個(gè)UI線程,一個(gè)后臺線程,一個(gè)業(yè)務(wù)先在后臺線程計(jì)算數(shù)據(jù),然后在UI線程中刷新顯示數(shù)據(jù),顯然不同的線程其上下文環(huán)境是不同的,兩個(gè)線程的通信可以使用SynchronizationContext完成。SynchronizationContext官方文檔 https://learn.microsoft.com/zh-CN/dotnet/api/system.threading.synchronizationcontext?view=netcore-3.0

協(xié)程鎖

多線程編程,對公共資源的訪問要加鎖,以保證數(shù)據(jù)訪問的安全。類似的,在ET的異步編程中,從雖然上文中可以了解到ET的異步其實(shí)是單線程的,從代碼運(yùn)行的層面其實(shí)是一個(gè)線程以某種順序處理一個(gè)個(gè)的任務(wù),但是這種“順序”并不可控。ET這里的協(xié)程鎖其實(shí)就是使用某個(gè)key,對所有用這個(gè)key包裹的代碼段推入一個(gè)隊(duì)列,只有前面的代碼段執(zhí)行結(jié)束才能執(zhí)行后面的代碼。

這看起來和C#平時(shí)用的lock(object),其實(shí)只是用法上比較像,其實(shí)在實(shí)現(xiàn)細(xì)節(jié)是有根本的差距的:簡單來說。ET實(shí)現(xiàn)的協(xié)程鎖是一種用戶態(tài)的鎖,不會(huì)造成內(nèi)核態(tài)/用戶態(tài)的切換。而lock是一種C#語法糖,在編譯時(shí)其實(shí)是通過Monitor監(jiān)視器實(shí)現(xiàn)的,會(huì)涉及到內(nèi)核轉(zhuǎn)換。一個(gè)線程上可能會(huì)運(yùn)行成百上千個(gè)協(xié)程,如果這個(gè)線程被掛起,那么有可能造成很多協(xié)程Delay,可能造成災(zāi)難性的后果。

結(jié)構(gòu)類圖:時(shí)序圖:結(jié)合ET工程官方的一個(gè)用法:

public static async ETTask Query(this DBComponent self, long id, string collection = null) where T : Entity{    using (await CoroutineLockComponent.Instance.Wait(CoroutineLockType.DB, id % DBComponent.TaskCount))    {        IAsyncCursor cursor = await self.GetCollection(collection).FindAsync(d => d.Id == id);        return await cursor.FirstOrDefaultAsync();    }}

可以看到協(xié)程鎖是被using包裹的,即{}包裹的代碼塊運(yùn)行結(jié)束,協(xié)程鎖會(huì)被dispose。先來看當(dāng)?shù)谝淮握{(diào)用Wait時(shí)會(huì)直接返回,當(dāng)?shù)谝淮蔚逆i沒有被dispose時(shí),后面獲取鎖時(shí)會(huì)進(jìn)入隊(duì)列。當(dāng)前面的鎖被dispose時(shí),會(huì)通知隊(duì)列中后面一個(gè)鎖在下一次Update時(shí)被Notify,SetResult獲取到鎖,其所屬的代碼段得以執(zhí)行。

關(guān)鍵詞:

專題首頁|財(cái)金網(wǎng)首頁

投資
探索

精彩
互動(dòng)

獨(dú)家
觀察

京ICP備2021034106號-38   營業(yè)執(zhí)照公示信息  聯(lián)系我們:55 16 53 8 @qq.com 關(guān)于我們 財(cái)金網(wǎng)  版權(quán)所有  cfenews.com