在线观看www成人影院-在线观看www日本免费网站-在线观看www视频-在线观看操-欧美18在线-欧美1级

0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

何選擇一個(gè)合適的協(xié)程來獲得CPU執(zhí)行權(quán)

科技綠洲 ? 來源:Linux開發(fā)架構(gòu)之路 ? 作者:Linux開發(fā)架構(gòu)之路 ? 2023-11-13 14:10 ? 次閱讀

如今雖不敢說協(xié)程已經(jīng)是紅的發(fā)紫,但確實(shí)是越來越受到了大家的重視。Golang中的已經(jīng)是只有g(shù)oroutine,以至于很多go程序員是只知有協(xié)程,不知有線程了。就連C++也在最新的C++20中原生支持協(xié)程。更不用說很多活躍的語(yǔ)言如pythonjava等,也都是支持協(xié)程的。盡管這些協(xié)程可能名稱不同,甚至用法也不同,但它們都可以被劃分為兩大類,一類是有(stackful) 協(xié)程,例如 goroutine,libco;一類是無棧 (stackless) 協(xié)程,例如C++的協(xié)程。

這里我們想說的一點(diǎn)是所謂的有棧,無棧并不是說這個(gè)協(xié)程運(yùn)行的時(shí)候有沒有棧,而是說協(xié)程之間是否存在調(diào)用棧(callbackStack)。其實(shí)仔細(xì)一想即可,但凡是個(gè)正在運(yùn)行的程序,不管你是協(xié)程也好,線程也好,怎么可能在運(yùn)行的時(shí)候不使用棧空間呢,調(diào)用參數(shù)往哪擱,局部變量往哪擱。我們知道基本所有的主流語(yǔ)言在調(diào)用另外一個(gè)函數(shù)的時(shí)候都存在一個(gè)調(diào)用棧,我們來解釋一下調(diào)用棧這個(gè)詞:

這幅圖是有兩個(gè)棧幀的調(diào)用棧,我在這篇文章中對(duì)棧幀下過定義,即:函數(shù)的棧幀是指esp和ebp之間的一塊地址。拿上圖來說ebp存儲(chǔ)著Frame Pointer指向的地址,Return Address當(dāng)然就是我們?cè)趫?zhí)行完最新的棧幀以后下一步要執(zhí)行的指令地址。esp當(dāng)然就是當(dāng)前指向棧頂?shù)闹羔樍恕?/p>

有棧協(xié)程

很多地方又把協(xié)程稱為subroutine,subroutine是什么,就是函數(shù)。上古時(shí)期的計(jì)算機(jī)科學(xué)家們?cè)缇徒o出了概念,coroutine就是可以中斷并恢復(fù)執(zhí)行的subroutine,從這個(gè)角度來看協(xié)程擁有調(diào)用棧并不是一個(gè)奇怪的事情。我們?cè)賮硭伎糲oroutine與subroutinue相比有什么區(qū)別,你會(huì)發(fā)現(xiàn)區(qū)別僅有一個(gè),就是coroutinue可以中斷并恢復(fù),對(duì)應(yīng)的操作就是yield/resume,這樣看來subroutinue不過是coroutinue的一個(gè)子集罷了。也就是說把協(xié)程當(dāng)做一個(gè)特殊的函數(shù)調(diào)用,有棧協(xié)程就是我們理想中協(xié)程該有的模樣。

既然把其當(dāng)做一個(gè)特殊的函數(shù)調(diào)用,對(duì)我們來說最嚴(yán)峻的挑戰(zhàn)就是如何像切換函數(shù)一樣去切換協(xié)程,難點(diǎn)在于除了像函數(shù)一樣切換出去,還要在某種條件滿足的時(shí)候切換回來,我們的做法可以是在協(xié)程內(nèi)部存儲(chǔ)自身的上下文,并在需要切換的時(shí)候把上下文切換就可以了,我們知道上下文其實(shí)本質(zhì)上就是寄存器,所以保存上下文實(shí)際上就是把寄存器的值保存下來,有兩種方法,一種是使用匯編,libco就使用了這種方法。還有一種是使用ucontext.h,這個(gè)封裝好的庫(kù)也可以幫我們完成相關(guān)工作。

匯編的話我們來看一看libco中對(duì)于32位機(jī)器的上下文切換操作是如何完成的:

// 獲取第一個(gè)參數(shù)
    movl 4(%esp), %eax 
    // 參數(shù)的類型我們暫且理解為一個(gè)擁有八個(gè)指針的數(shù)組,即regs
	| regs[7] |
	| regs[6] |
	| regs[5] |
	| regs[4] |
	| regs[3] |
	| regs[2] |
	| regs[1] |
	| regs[0] |
	--------------   < ---EAX

    movl %esp,  28(%eax)  
    movl %ebp, 24(%eax)
    movl %esi, 20(%eax)
    movl %edi, 16(%eax)
    movl %edx, 12(%eax)
    movl %ecx, 8(%eax)
    movl %ebx, 4(%eax)
	// 想想看,這里eax加偏移不就是對(duì)應(yīng)了regs中的值嗎?這樣就把所有寄存器中的值保存在了參數(shù)中

	
	// ESP偏移八位就是第二個(gè)參數(shù)的偏移了,這樣我們就可以把第二個(gè)參數(shù)regs中的上下文切換到寄存器中了
    movl 8(%esp), %eax 
    movl 4(%eax), %ebx
    movl 8(%eax), %ecx
    movl 12(%eax), %edx  
    movl 16(%eax), %edi
    movl 20(%eax), %esi
    movl 24(%eax), %ebp
    movl 28(%eax), %esp

	ret
	// 這樣我們就完成了一次協(xié)程的切換

我們可以看到其實(shí)就是參數(shù)中傳入兩個(gè)協(xié)程的上下文結(jié)構(gòu),然后第一個(gè)參數(shù)執(zhí)行保存上下文,然后把第二個(gè)參數(shù)的上下文存入寄存器,這樣就執(zhí)行了兩個(gè)協(xié)程的切換。

當(dāng)然我們上面提到了調(diào)用棧,那么既然有調(diào)用棧,那么肯定有一個(gè)執(zhí)行的順序,即一定要把棧頂?shù)膮f(xié)程全部運(yùn)行完才可以運(yùn)行下一層的協(xié)程,這樣說可能比較抽象,我們舉一個(gè)簡(jiǎn)單的例子:

主協(xié)程A中執(zhí)行協(xié)程B,此時(shí)調(diào)用棧是在[A,B]和[A]之間切換,因?yàn)锽會(huì)主動(dòng)讓出執(zhí)行權(quán),然后調(diào)用棧上此時(shí)就只有一個(gè)A了

B協(xié)程中執(zhí)行C,D協(xié)程,此時(shí)調(diào)用棧是在[A,B,C],[A,B],[A,B,D]之間轉(zhuǎn)換的,

這樣看來我們總是只能在調(diào)用棧頂?shù)膮f(xié)程運(yùn)行完以后才能去執(zhí)行更低一層的協(xié)程,當(dāng)然,這也是典型的非對(duì)稱協(xié)程,即協(xié)程之間有明顯的調(diào)用關(guān)系。

當(dāng)然在我的描述中也可以看出有棧協(xié)程涉及到對(duì)于寄存器的保存和修改,也涉及到對(duì)每一個(gè)協(xié)程棧(實(shí)際運(yùn)行的棧)的分配。對(duì)于寄存器來說,現(xiàn)代寄存器基本都是上百個(gè)字節(jié)的數(shù)據(jù),還有每一個(gè)協(xié)程的棧,如果選擇了共享?xiàng)#稚婕暗綄?duì)棧上數(shù)據(jù)的拷貝,顯然在效率上來說相比無棧協(xié)程的確是有一些損失的。

無棧協(xié)程

那么所謂的無棧協(xié)程是什么呢?其實(shí)無棧協(xié)程的本質(zhì)就是一個(gè)狀態(tài)機(jī)(state machine),它可以理解為在另一個(gè)角度去看問題,即同一協(xié)程協(xié)程的切換本質(zhì)不過是指令指針寄存器的改變。這里推薦一篇文章,其內(nèi)容是用C語(yǔ)言實(shí)現(xiàn)一個(gè)協(xié)程,其實(shí)就是一個(gè)無棧協(xié)程的實(shí)現(xiàn)。

我們來看一個(gè)使用libco的協(xié)程的例子,當(dāng)然libco是一個(gè)有棧協(xié)程:

void* test(void* para){
	co_enable_hook_sys();
	int i = 0;
	poll(0, 0, 0. 1000); // 協(xié)程切換執(zhí)行權(quán),1000ms后返回
	i++;
	poll(0, 0, 0. 1000); // 協(xié)程切換執(zhí)行權(quán),1000ms后返回
	i--;
	return 0;
}

int main(){
	stCoRoutine_t* routine;
	co_create(&routine, NULL, test, 0);// 創(chuàng)建一個(gè)協(xié)程
	co_resume(routine); 
	co_eventloop( co_get_epoll_ct(),0,0 );
	return 0;
}

這段代碼實(shí)際的意義就是主協(xié)程跑一個(gè)協(xié)程去執(zhí)行test函數(shù),在test中我們需要兩次從協(xié)程中切換出去,這里對(duì)應(yīng)了兩個(gè)poll操作(hook機(jī)制,有興趣的朋友可以點(diǎn)擊這里),hook后的poll所做的事情就是把當(dāng)前協(xié)程的CPU執(zhí)行權(quán)切換到調(diào)用棧的上一層,并在超時(shí)或注冊(cè)的fd就緒時(shí)返回(當(dāng)然樣例這里就只是超時(shí)了)。那么無棧協(xié)程跑相同的代碼是怎么樣的呢?其實(shí)就是翻譯成類似于以下代碼:

struct test_coroutine {
    int i;
    int __state = 0;
    void MoveNext() {
        switch(__state) {
        case 0:
            return frist();
        case 1:
            return second();
        case 2:
        	return third();
        }
    }
    void frist() {
        i = 0;
        __state = 1;
    }
    void second() {
        i++;
        _state = 2;
    }
    void third() {
    	i--;
    }
};

我們可以看到相比與有棧協(xié)程中的test函數(shù),這里把整個(gè)協(xié)程抽象成一個(gè)類,以原本需要執(zhí)行切換的語(yǔ)句處為界限,把函數(shù)劃分為幾個(gè)部分,并在某一個(gè)部分執(zhí)行完以后進(jìn)行狀態(tài)轉(zhuǎn)移,在下一次調(diào)用此函數(shù)的時(shí)候就會(huì)執(zhí)行下一部分,這樣的話我們就完全沒有必要像有棧協(xié)程那樣顯式的執(zhí)行上下文切換了,我們只需要一個(gè)簡(jiǎn)易的調(diào)度器來調(diào)度這些函數(shù)即可。

從執(zhí)行時(shí)棧的角度來看,其實(shí)所有的協(xié)程共用的都是一個(gè)棧,即系統(tǒng)棧,也就也不必我們自行去給協(xié)程分配棧,因?yàn)槭呛瘮?shù)調(diào)用,我們當(dāng)然也不必去顯示的保存寄存器的值,而且相比有棧協(xié)程把局部變量放在新開的空間上,無棧協(xié)程直接使用系統(tǒng)棧使得CPU cache局部性更好,同時(shí)也使得無棧協(xié)程的中斷和函數(shù)返回幾乎沒有區(qū)別,這樣也可以凸顯出無棧協(xié)程的高效。

對(duì)稱協(xié)程與非對(duì)稱協(xié)程

其實(shí)對(duì)于“對(duì)稱”這個(gè)名詞,闡述的實(shí)際是協(xié)程之間的關(guān)系,用大白話來說就是對(duì)稱協(xié)程就是說協(xié)程之間人人平等,沒有誰(shuí)調(diào)用誰(shuí)一說,大家都是一樣的,而非對(duì)稱協(xié)程就是協(xié)程之間存在明顯的調(diào)用關(guān)系。

簡(jiǎn)單來說就是這樣:

  • 對(duì)稱協(xié)程 Symmetric Coroutine:任何一個(gè)協(xié)程都是相互獨(dú)立且平等的,調(diào)度權(quán)可以在任意協(xié)程之間轉(zhuǎn)移。
  • 非對(duì)稱協(xié)程 Asymmetric Coroutine:協(xié)程出讓調(diào)度權(quán)的目標(biāo)只能是它的調(diào)用者,即協(xié)程之間存在調(diào)用和被調(diào)用關(guān)系。

其實(shí)兩者的實(shí)現(xiàn)我覺得其實(shí)差異不大,非對(duì)稱協(xié)程其實(shí)就是擁有調(diào)用棧,而非對(duì)稱協(xié)程則是大家都平等,不需要調(diào)用棧,只需要一個(gè)數(shù)據(jù)結(jié)構(gòu)存儲(chǔ)所有未執(zhí)行完的協(xié)程即可。至于哪種更優(yōu)?我覺得分情況,如果你使用協(xié)程的目的是為了優(yōu)化一些IO密集型應(yīng)用,那么協(xié)程切換出去的時(shí)候就是它等待事件到來的時(shí)候,此時(shí)你就算切換過去也沒有什么意義,還不如等到事件到來的時(shí)候自動(dòng)切換回去。

其實(shí)上面說的是有一些問題,因?yàn)檫@個(gè)執(zhí)行權(quán)的切換實(shí)際上是(調(diào)用者–被調(diào)用者)之間的切換,對(duì)稱就是它們之間都是平等的,就是假如A協(xié)程執(zhí)行了B,C協(xié)程,那么B協(xié)程可以切換回A,也可以切換回C。而非對(duì)稱只能是B切換回A,A切換回C,C再切換回A,以此類推。

這樣看起來顯然非對(duì)稱協(xié)程相比之下更為符合我們的認(rèn)知,因?yàn)閷?duì)稱協(xié)程目前我不知道如何選擇一個(gè)合適的協(xié)程來獲得CPU執(zhí)行權(quán),正如上面所說,此協(xié)程可能正在等待事件。當(dāng)然如果調(diào)度算法足夠優(yōu)秀的話,對(duì)稱協(xié)程也是可取的。

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • cpu
    cpu
    +關(guān)注

    關(guān)注

    68

    文章

    10863

    瀏覽量

    211781
  • 程序
    +關(guān)注

    關(guān)注

    117

    文章

    3787

    瀏覽量

    81049
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4331

    瀏覽量

    62622
  • C++
    C++
    +關(guān)注

    關(guān)注

    22

    文章

    2108

    瀏覽量

    73651
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    談?wù)?b class='flag-5'>協(xié)的那些事兒

    隨著異步編程的發(fā)展以及各種并發(fā)框架的普及,協(xié)作為種異步編程規(guī)范在各類語(yǔ)言中地位逐步提高。我們不單單會(huì)在自己的程序中使用協(xié),各類框架如f
    的頭像 發(fā)表于 01-26 11:36 ?1119次閱讀
    談?wù)?b class='flag-5'>協(xié)</b><b class='flag-5'>程</b>的那些事兒

    協(xié)和線程有什么區(qū)別

    協(xié)和線程的區(qū)別協(xié)和線程的共同目的之是實(shí)現(xiàn)系統(tǒng)資源的上下文調(diào)用,不過它們的實(shí)現(xiàn)層級(jí)不同;線程(Thraed)是比進(jìn)程小
    發(fā)表于 12-10 06:23

    Python中的多核CPU共享數(shù)據(jù)之協(xié)詳解

    協(xié)又稱微線程,coroutne,協(xié)種用戶態(tài)的輕量級(jí)線程。通俗點(diǎn)講就是周末我在家里休息,假如我先洗漱,再煮飯,再下載電影看會(huì)很慢,用了
    的頭像 發(fā)表于 12-07 10:23 ?6631次閱讀
    Python中的多核<b class='flag-5'>CPU</b>共享數(shù)據(jù)之<b class='flag-5'>協(xié)</b><b class='flag-5'>程</b>詳解

    Python自動(dòng)化運(yùn)維之協(xié)函數(shù)賦值過程

    及同步的開銷(3)方便切換控制流,簡(jiǎn)化編程模型(4)高并發(fā)+高擴(kuò)展性+低成本:個(gè)CPU支持上萬的協(xié)都不是問題。所以很適合用于高并發(fā)處理。
    的頭像 發(fā)表于 03-18 11:22 ?3732次閱讀

    關(guān)于C++ 20協(xié)最全面詳解

    花了一兩周的時(shí)間后,我想寫寫 C++20 協(xié)的基本用法,因?yàn)?C++ 的協(xié)讓我感到很奇怪,寫個(gè)協(xié)
    的頭像 發(fā)表于 04-12 11:10 ?1.3w次閱讀
    關(guān)于C++ 20<b class='flag-5'>協(xié)</b><b class='flag-5'>程</b>最全面詳解

    Python后端項(xiàng)目的協(xié)是什么

    最近公司 Python 后端項(xiàng)目進(jìn)行重構(gòu),整個(gè)后端邏輯基本都變更為采用“異步”協(xié)的方式實(shí)現(xiàn)。看著滿屏幕經(jīng)過 async await(協(xié)在 Python 中的實(shí)現(xiàn))修飾的代碼,我頓時(shí)
    的頭像 發(fā)表于 09-23 14:38 ?1333次閱讀

    Python協(xié)與JavaScript協(xié)的對(duì)比及經(jīng)驗(yàn)技巧

    對(duì)這兩個(gè)語(yǔ)言有興趣的新人理解和吸收。 共同訴求隨著 cpu 多核化,都需要實(shí)現(xiàn)由于自身歷史原因(單線程環(huán)境)下的并發(fā)功能 簡(jiǎn)化代碼,避免回調(diào)地獄,關(guān)鍵字支持 有效利用操作系統(tǒng)資源和硬件:協(xié)
    的頭像 發(fā)表于 10-20 14:30 ?1939次閱讀

    通過例子由淺入深的理解yield協(xié)

    send:send() 方法致使協(xié)程前進(jìn)到下一個(gè)yield 語(yǔ)句,另外,生成器可以作為協(xié)使用
    的頭像 發(fā)表于 08-23 11:12 ?2031次閱讀

    協(xié)的概念及協(xié)的掛起函數(shù)介紹

    協(xié)種輕量級(jí)的線程,它可以在單個(gè)線程中實(shí)現(xiàn)并發(fā)執(zhí)行。與線程不同,協(xié)不需要操作系統(tǒng)的上下文切
    的頭像 發(fā)表于 04-19 10:20 ?893次閱讀

    Kotlin協(xié)實(shí)戰(zhàn)進(jìn)階之筑基篇1

    。 Android 中的每個(gè)應(yīng)用都會(huì)運(yùn)行個(gè)主線程,它主要是用來處理 UI,如果主線程上需要處理的任務(wù)太多,應(yīng)用就感覺被卡主樣影響用戶體驗(yàn),得讓那些耗時(shí)的任務(wù)不阻塞主線程的運(yùn)行。要做到處理網(wǎng)絡(luò)請(qǐng)求不會(huì)阻塞主線程,
    的頭像 發(fā)表于 05-30 16:24 ?717次閱讀
    Kotlin<b class='flag-5'>協(xié)</b><b class='flag-5'>程</b>實(shí)戰(zhàn)進(jìn)階之筑基篇1

    Kotlin協(xié)實(shí)戰(zhàn)進(jìn)階之筑基篇3

    。 Android 中的每個(gè)應(yīng)用都會(huì)運(yùn)行個(gè)主線程,它主要是用來處理 UI,如果主線程上需要處理的任務(wù)太多,應(yīng)用就感覺被卡主樣影響用戶體驗(yàn),得讓那些耗時(shí)的任務(wù)不阻塞主線程的運(yùn)行。要做到處理網(wǎng)絡(luò)請(qǐng)求不會(huì)阻塞主線程,
    的頭像 發(fā)表于 05-30 16:26 ?700次閱讀

    FreeRTOS任務(wù)與協(xié)介紹

    FreeRTOS 中應(yīng)用既可以使用任務(wù),也可以使用協(xié)(Co-Routine),或者兩者混合使用。但是任務(wù)和協(xié)使用不同的API函數(shù),因此不能通過隊(duì)列(或信號(hào)量)將數(shù)據(jù)從任務(wù)發(fā)送給協(xié)
    的頭像 發(fā)表于 09-28 11:02 ?998次閱讀

    協(xié)的作用、結(jié)構(gòu)及原理

    本文介紹了協(xié)的作用、結(jié)構(gòu)、原理,并使用C++和匯編實(shí)現(xiàn)了64位系統(tǒng)下的協(xié)池。文章內(nèi)容避免了協(xié)
    的頭像 發(fā)表于 11-08 16:39 ?1142次閱讀
    <b class='flag-5'>協(xié)</b><b class='flag-5'>程</b>的作用、結(jié)構(gòu)及原理

    C/C++協(xié)編程的相關(guān)概念和技巧

    、引言 協(xié)的定義和背景 協(xié)(Coroutine),又稱為微線程或者輕量級(jí)線程,是種用戶態(tài)
    的頭像 發(fā)表于 11-09 11:34 ?796次閱讀

    協(xié)的實(shí)現(xiàn)與原理

    前言 協(xié)這個(gè)概念很久了,好多程序員是實(shí)現(xiàn)過這個(gè)組件的,網(wǎng)上關(guān)于協(xié)的文章,博客,論壇都是汗牛充棟,在知乎,github上面也有很多大牛寫了關(guān)于協(xié)
    的頭像 發(fā)表于 11-10 10:57 ?443次閱讀
    主站蜘蛛池模板: 亚洲狠狠婷婷综合久久久久图片| 欧美色插| 久久婷婷久久一区二区三区| 欧美国产在线一区| 日本久本草精品| 女人张开腿让男人桶免费最新 | 国产精品7m凸凹视频分类大全 | 天天操丝袜| 日日噜噜夜夜狠狠久久丁香| 欧美巨大bbbb动漫| 天天色天天干天天| 国产午夜精品福利| 中文字幕一区二区三区在线播放| 人人澡人人添| 亚洲免费视频在线观看| 日本三浦理惠子中文字幕| 777色狠狠一区二区三区香蕉| 天天射天天操天天干| 一区二区影视| 天堂在线视频精品| 爱爱欧美| 久久久久久久成人午夜精品福利| 色琪琪一本到影院| 在线免费视频你懂的| 日日噜噜噜噜人人爽亚洲精品| 闲人综合| 88av视频在线观看| 久久久久国产一级毛片高清板| 一女被两男吃奶玩乳尖口述| 视频一区二区在线| 日本欧美一区二区免费视| 日本成片免费高清| 免费澳门一级毛片| 国产青青草| www.婷婷.com| 午夜毛片视频| 天天操夜夜做| 国产又爽又黄又粗又大| 亚洲第九页| www.妖精视频| 69日本人xxxx16-18|