溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Goroutine怎么理解

發(fā)布時(shí)間:2022-01-10 11:35:42 來(lái)源:億速云 閱讀:162 作者:柒染 欄目:開發(fā)技術(shù)

今天給大家介紹一下Goroutine怎么理解。文章的內(nèi)容小編覺(jué)得不錯(cuò),現(xiàn)在給大家分享一下,覺(jué)得有需要的朋友可以了解一下,希望對(duì)大家有所幫助,下面跟著小編的思路一起來(lái)閱讀吧。

Go語(yǔ)言里面的并發(fā)使用的是Goroutine,Goroutine可以看做一種輕量級(jí)的線程,或者叫用戶級(jí)線程。與JavaThread很像,用法很簡(jiǎn)單:

go fun(params);

相當(dāng)于Java的

new Thread(someRunnable).start();

雖然類似,但是GoroutineJava Thread有著很大的區(qū)別。

Java里的Thread使用的是線程模型的一對(duì)一模型,每一個(gè)用戶線程都對(duì)應(yīng)著一個(gè)內(nèi)核級(jí)線程。

Goroutine怎么理解

上圖有兩個(gè)CPU,然后有4個(gè)Java thread,每個(gè)Java thread其實(shí)就是一個(gè)內(nèi)核級(jí)線程,由內(nèi)核級(jí)線程調(diào)度器進(jìn)行調(diào)度,輪流使用兩個(gè)CPU。內(nèi)核級(jí)線程調(diào)度器具有絕對(duì)的權(quán)力,所以把它放到了下面。內(nèi)核級(jí)線程調(diào)度器使用公平的算法讓四個(gè)線程使用兩個(gè)CPU。

 Goroutine怎么理解

Go的Goroutine是用戶級(jí)的線程。同樣是4個(gè)Goroutine,可能只對(duì)應(yīng)了兩個(gè)內(nèi)核級(jí)線程。Goroutine調(diào)度器把4個(gè)Goroutine分配到兩個(gè)內(nèi)核級(jí)線程上,而這兩個(gè)內(nèi)核級(jí)線程對(duì)CPU的使用由內(nèi)核線程調(diào)度器來(lái)分配。

與內(nèi)核級(jí)線程調(diào)度器相比,Goroutine的調(diào)度器與Goroutine是平等的,所以把它和Goroutine放到了同一個(gè)層次。調(diào)度器與被調(diào)度者權(quán)力相同,那被調(diào)度者就可以不聽話了。一個(gè)Goroutine如果占據(jù)了CPU就是不放手,調(diào)度器也拿它沒(méi)辦法。

同樣是下面一段代碼:

void run() {
  int a = 1;
  while(1==1) {
   a = 1;
  }
}

在Java里,如果起多個(gè)這樣的線程,它們可以平等的使用CPU。但是在Go里面,如果起多個(gè)這樣的Goroutine,在啟動(dòng)的內(nèi)核級(jí)線程個(gè)數(shù)一定情況下(通常與CPU個(gè)數(shù)相等),那么最先啟動(dòng)的Goroutine會(huì)一直占據(jù)CPU,其它的Goroutine會(huì)starve,餓死,因?yàn)樗荒苤鲃?dòng)放棄CPU,不配合別人工作。說(shuō)到配合工作,那就需要說(shuō)一下協(xié)程(coroutine,可以當(dāng)做cooperative routine),協(xié)程需要相互合作,互相協(xié)助,才能正常工作,所以叫做協(xié)程。

 Goroutine怎么理解

 協(xié)程并不需要一個(gè)調(diào)度器,它是完全靠互相之間協(xié)調(diào)來(lái)工作的。協(xié)程的定義在學(xué)術(shù)上很抽象,目前實(shí)際應(yīng)用中,協(xié)程通常是使用單個(gè)內(nèi)核級(jí)線程,用來(lái)把異步編程中使用的難懂的callback方式改成看上去像同步編程的樣子。

比如nodejs是異步單線程事件驅(qū)動(dòng)的,在一段代碼中如果有多次異步操作,比如先調(diào)用一個(gè)支付系統(tǒng),得到結(jié)果后再更新數(shù)據(jù)庫(kù),那么可能需要嵌套使用callback。pay函數(shù)是一個(gè)調(diào)用支付系統(tǒng)的操作,異步發(fā)出請(qǐng)求后就返回,然后等支付完成的事件后觸發(fā)第一個(gè)回調(diào)函數(shù),這個(gè)函數(shù)是更新數(shù)據(jù)庫(kù),又是一個(gè)異步操作,等這個(gè)異步操作完成后,再次觸發(fā)返回更新結(jié)果的回調(diào)函數(shù)。 這里只有兩個(gè)異步操作,如果多的話,有可能會(huì)有很多嵌套。

pay(amount, callback(payamount) {
 update(payamount, callback(result) {
   return result;
})});

而使用協(xié)程,可以看上去像是同步操作

pay(amount){
  //異步,立刻返回
  //payamount需要操作完成后才能被賦值
 payamount = dopay(amount);
 yeild main;//把控制權(quán)返回主routine
 //dopay事件完成后,主routine會(huì)調(diào)起這個(gè)routine,
 //繼續(xù)執(zhí)行doupdate
 result=doupdate(payamount); 
 yeild main;  //再次把控制權(quán)返回主routine
 return result;
}

(以上都是偽代碼)

把原來(lái)的各種嵌套callback改成協(xié)程,那么邏輯就會(huì)清晰很多。

GoroutineCoroutine不一樣,開發(fā)者并不需要關(guān)心Goroutine如何被調(diào)起,如何放棄控制權(quán),而是交給Goroutine調(diào)度器來(lái)管理。開發(fā)者不用關(guān)心,但是Go語(yǔ)言的編譯器會(huì)替你把工作做了,因?yàn)?/span>Goroutine必須主動(dòng)交出控制權(quán)才能由調(diào)度器統(tǒng)一管理。首先我們可以認(rèn)為寫上面那種死循環(huán)而且不調(diào)用任何其他函數(shù)的Goroutine是沒(méi)意義的,如果真在實(shí)際應(yīng)用中寫出這樣的代碼,那開發(fā)者不是一個(gè)合格的程序員。一個(gè)Goroutine總會(huì)調(diào)用其他函數(shù)的,一種調(diào)用是開發(fā)者自己寫的函數(shù),一種是Go語(yǔ)言提供的API。那編譯器以及這些API就可以做文章了。

比如

void run() {
  int a = 0;
  int b = 1;
  a = b * 2;
  for(int i = 0; i < 100; i++) {
    a = func1(a);
 }
}

那么編譯器可能會(huì)在調(diào)用其他函數(shù)的地方偷偷加上幾條語(yǔ)句,比如:

void run() {
  int a = 0;
  int b = 1;
  a = b * 2;
  for(int i = 0; i < 100; i++) {
   //進(jìn)入調(diào)度器,或者以一定概率進(jìn)入調(diào)度器
   schedule();  
   a = func1(a);
  }
}

再比如

void run() {
  socket = new socket();
 while(buffer = socker.read()) {
  deal(buffer);
 }
}

socker.read()Go語(yǔ)言提供的一個(gè)系統(tǒng)函數(shù),那么Go語(yǔ)言可能在這里面加點(diǎn)操作,讀完數(shù)據(jù)后,進(jìn)入調(diào)度器,讓調(diào)度器決定這個(gè)Goroutine是否繼續(xù)跑。

下面這段Go語(yǔ)言代碼,把內(nèi)核級(jí)線程設(shè)成2個(gè),那么主線程會(huì)餓死,而在func1里加一個(gè)sleep就可以了,這樣func1才有機(jī)會(huì)放棄控制權(quán)。

Goroutine怎么理解

當(dāng)然Go語(yǔ)言的調(diào)度器要比這復(fù)雜的多。Goroutine與協(xié)程還是有區(qū)別的,實(shí)現(xiàn)原理是一樣的,但是Goroutine的目的是為了實(shí)現(xiàn)并發(fā),在Go語(yǔ)言里,開發(fā)者不能創(chuàng)建內(nèi)核級(jí)線程,只能創(chuàng)建Goroutine,而協(xié)程的目的如上面所示,目前比較常見的用途就是上面這個(gè)。Go語(yǔ)言適合編寫高并發(fā)的應(yīng)用,因?yàn)閯?chuàng)建一個(gè)Goroutine的代價(jià)很低,而且Goroutine切換上下文開銷也很低,與創(chuàng)建內(nèi)核級(jí)線程相比,Goroutine的開銷可能只是幾十分之一甚至幾百分之一,而且它不占內(nèi)核空間,每個(gè)內(nèi)核級(jí)線程都會(huì)占很大的內(nèi)核空間,能創(chuàng)建的線程數(shù)最多也就幾千個(gè),而Goroutine可以很輕松的創(chuàng)建上萬(wàn)個(gè)

Goroutine底層的實(shí)現(xiàn),在Linux上面是用makecontext,swapcontext,getcontext,setcontext這幾個(gè)函數(shù)實(shí)現(xiàn)的,這幾個(gè)系統(tǒng)調(diào)用可以實(shí)現(xiàn)用戶空間線程上下文的保存和切換。

以上就是Goroutine怎么理解的全部?jī)?nèi)容了,更多與Goroutine怎么理解相關(guān)的內(nèi)容可以搜索億速云之前的文章或者瀏覽下面的文章進(jìn)行學(xué)習(xí)哈!相信小編會(huì)給大家增添更多知識(shí),希望大家能夠支持一下億速云!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI