溫馨提示×

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

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

sync.Wait()函數(shù)無(wú)法在golang中執(zhí)行如何解決

發(fā)布時(shí)間:2020-12-23 13:58:55 來(lái)源:億速云 閱讀:197 作者:Leah 欄目:開(kāi)發(fā)技術(shù)

sync.Wait()函數(shù)無(wú)法在golang中執(zhí)行如何解決?很多新手對(duì)此不是很清楚,為了幫助大家解決這個(gè)難題,下面小編將為大家詳細(xì)講解,有這方面需求的人可以來(lái)學(xué)習(xí)下,希望你能有所收獲。

goroutine 似乎不用解釋太多,可以利用它實(shí)現(xiàn)多線程,也可以利用它來(lái)實(shí)現(xiàn)異步事件。

在使用關(guān)鍵字go的過(guò)程中,常常會(huì)將用到sync.WaitGroup,如下一段代碼。

package main
import (
	"fmt"
	"sync"
	"time"
)
func Run() {
	var wg = &sync.WaitGroup{}
	go func() {
		wg.Add(1)
		fmt.Println("halo world start")
		time.Sleep(time.Second * 5)
		fmt.Println("halo world end")
		wg.Done()
	}()
	// time.Sleep(time.Millisecond * 5)
	// fmt.Println("server will start")
	wg.Wait()
}
func main() {
	Run()
}
// output:
//

期待的結(jié)果是打印 halo world start,5秒后打印halo world end,但是結(jié)果就是什么都沒(méi)有,并且進(jìn)程立即就結(jié)束了。

原因

關(guān)鍵字go是異步的,當(dāng)執(zhí)行到go,不會(huì)立即執(zhí)行g(shù)o 后面的內(nèi)容,而且繼續(xù)往下執(zhí)行。此時(shí)wg.Add(1)還沒(méi)有來(lái)得及執(zhí)行,wg.Wait()就已經(jīng)執(zhí)行,即不會(huì)發(fā)生等待,進(jìn)程就結(jié)束了。

怎么解決:

只需要在wg.Wait()前有其他操作,給與足夠的時(shí)間讓wg.Add(1)執(zhí)行即可,

方法一、時(shí)間等待,在wg.Wait()前加一句time.Sleep(time.Millisecond*5),既不影響性能,也能讓wg.Add(1)來(lái)得及執(zhí)行

方法二、有IO操作,在wg.Wait()有其他IO操作,比如fmt.Println("server will start"),原因是std的輸出會(huì)將進(jìn)程從用戶(hù)態(tài)轉(zhuǎn)向內(nèi)核態(tài),打印命令發(fā)出后,又切回用戶(hù)態(tài),這個(gè)狀態(tài)的轉(zhuǎn)換是很有消耗的,wg.Add(1)也就有時(shí)間執(zhí)行。

Don't worry

是否有存在擔(dān)心,方法一的時(shí)間等待,等待的時(shí)候不夠長(zhǎng),還是讓wg.Add(1)來(lái)不及執(zhí)行。don't worry.

這里涉及到goroutine的調(diào)度問(wèn)題,go進(jìn)程在執(zhí)行過(guò)程中,必須從goroutine隊(duì)列中取出一個(gè)來(lái)執(zhí)行,當(dāng)wg.Wait()執(zhí)行前就算執(zhí)行time.Sleep(time.Nanosecond), 一納秒,一…一…一納秒,wg.Add(1)也來(lái)得及執(zhí)行,因?yàn)橹鱣oroutine會(huì)被切換到睡眠狀態(tài),go進(jìn)程必須要取一個(gè)線程來(lái)執(zhí)行,就會(huì)取到wg.Add(1)這個(gè)線程,接下來(lái)就順理成章了。

同時(shí)方法二也是異曲同工,當(dāng)發(fā)出打印的事件,整個(gè)進(jìn)程都會(huì)被切換到就緒態(tài),然后再被cpu執(zhí)行。

補(bǔ)充:【golang】sync.WaitGroup{}的wait()調(diào)用位置不同導(dǎo)致意想不到錯(cuò)誤

協(xié)程go多了,總覺(jué)的天下我有,沒(méi)事就喜歡go一個(gè)協(xié)程,信手拈來(lái),在項(xiàng)目中寫(xiě)個(gè)如下類(lèi)似代碼:

  wh := sync.WaitGroup{}
  out := make(chan string)
  go func() {
    wh.Wait()
    close(out)
  }()
  go func() {
    for i := 0; i < 2; i++ {
      wh.Add(1)
      go tt(out)
      wh.Done()
    }
  }()

想著開(kāi)個(gè)協(xié)程去wait所有協(xié)程組,測(cè)試一下通了,沒(méi)問(wèn)題,好牛逼,協(xié)程呀!!

可多測(cè)試即便就會(huì)出現(xiàn):

send close channel

或者協(xié)程定死在某一個(gè),還自已為是的認(rèn)為自己寫(xiě)的子方法估計(jì)不小心關(guān)閉了channel,找了半天只找到在wg.wait()后進(jìn)行了關(guān)閉。就這樣扣了好久,還沒(méi)想到自己畫(huà)蛇添足的錯(cuò)誤,經(jīng)大佬一指點(diǎn),原來(lái)開(kāi)一個(gè)協(xié)程,還沒(méi)等后一個(gè)協(xié)程進(jìn)行wg.add(1)操作,wg.wait()就已經(jīng)過(guò)了,關(guān)閉了channel。

只好老老實(shí)實(shí)寫(xiě):

  wh := sync.WaitGroup{}
  out := make(chan string)
  go func() {
    for i := 0; i < 2; i++ {
      wh.Add(1)
      go tt(out)
      wh.Done()
    }
    wh.Wait()
    close(out)
  }()

看完上述內(nèi)容是否對(duì)您有幫助呢?如果還想對(duì)相關(guān)知識(shí)有進(jìn)一步的了解或閱讀更多相關(guān)文章,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝您對(duì)億速云的支持。

向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