小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

22 Go常見(jiàn)的并發(fā)模式和并發(fā)模型

 F2967527 2021-04-28

Go并發(fā)模型 

傳統(tǒng)的編程語(yǔ)言C++ Java Python等,他們的并發(fā)邏輯多事基于操作系統(tǒng)的線程。并發(fā)執(zhí)行單元(線程)之間的通信利用的就是操作系統(tǒng)提供的線程或進(jìn)程間通信的原語(yǔ)。如:共享內(nèi)存、信號(hào)、管道、消息隊(duì)列、套接字等。在這些通信原語(yǔ)中,使用最廣泛的就是共享內(nèi)存。 

如果你使用過(guò)這種共享內(nèi)存的并發(fā)模型,其實(shí)是難用的和容易發(fā)生錯(cuò)誤的,特別是在大型或復(fù)雜的業(yè)務(wù)場(chǎng)景中。 

Go語(yǔ)言從程序設(shè)計(jì)當(dāng)初,就將解決上面?zhèn)鹘y(tǒng)并發(fā)模型問(wèn)題作為目標(biāo),并在新并發(fā)模型設(shè)計(jì)中借鑒注明的CSP(Communicationing Sequential Processes-通信順序進(jìn)程)并發(fā)模型。 

CSP模型目的在于簡(jiǎn)化并發(fā)程序的編寫(xiě),讓并發(fā)程序的編寫(xiě)順序與編寫(xiě)順序程序一樣簡(jiǎn)單。 

生產(chǎn)者  —》輸出數(shù)據(jù)輸入/輸出原語(yǔ)》輸出數(shù)據(jù) 

為了實(shí)現(xiàn)CSP模型,GO語(yǔ)言引入了Channel.Goroutine可以讀寫(xiě)channel中的數(shù)據(jù),通過(guò)channelgoroutine組合連接在一起。 

Go語(yǔ)言中CSP雖然是主流并發(fā)模型,但是還是支持共享內(nèi)存并發(fā)模型。主要是在sync包中的互斥鎖、讀寫(xiě)鎖、條件變量、原子操作等。那么我們?cè)撊绾芜x擇呢? 

第一種:創(chuàng)建模式 

通常會(huì)使用下面的方式: 

type Worker struct { 

func Do(f func()) chan Worker { 

    w:= make(chan Worker) 

    go func() { 

        f() 

        w<-Worker{} 

    }() 

    return w 

func main() { 

    c:=Do(func() { 

        fmt.Print('到下班時(shí)間了...') 

    }) 

    <-c 

Do函數(shù)內(nèi)部創(chuàng)建了一個(gè)gorutine并且返回了一個(gè)channel類(lèi)型的變量。Do函數(shù)創(chuàng)建的新goroutine與調(diào)用的Do函數(shù)的goroutine之間通過(guò)一個(gè)channel聯(lián)系了起來(lái),2個(gè)goroutine可以通過(guò)channel進(jìn)行通訊。Do函數(shù)的實(shí)現(xiàn)因?yàn)?span>channelGo語(yǔ)言中是一等公民,channel可以像變量一樣初始化、傳遞和賦值。上面的例子Do返回了一個(gè)變量,這個(gè)變量就是通道,實(shí)現(xiàn)了主goroutine和子goroutine的通信。 

第二種:退出模式 

    a) 分離模式 

        分離模式使用最廣泛的是goroutine退出模式。所謂分離模式就是創(chuàng)建它的goroutine不需要關(guān)心它的退出,這類(lèi)goroutine啟動(dòng)后與其創(chuàng)建者徹底分離,其生命周期與其執(zhí)行的主函數(shù)相關(guān),函數(shù)返回即goroutine退出。 

        場(chǎng)景1:一次性任務(wù) 

// $GOROOT/src/net/dial.go 

func (d *Dialer) DialContext(ctx context.Context, network, address string) (Conn, error) { 

    ... ... 

        if oldCancel := d.Cancel; oldCancel != nil { 

                subCtx, cancel := context.WithCancel(ctx) 

                defer cancel() 

                go func() { 

                        select { 

                        case <-oldCancel: 

                                cancel() 

                        case <-subCtx.Done(): 

                        } 

                }() 

                ctx = subCtx 

        } 

    ... ... 

DialContext方法中創(chuàng)建了一個(gè)goroutine,用來(lái)監(jiān)聽(tīng)量個(gè)channel是否有數(shù)據(jù),一旦有數(shù)據(jù),處理后即退出。 

場(chǎng)景2 常駐后臺(tái)執(zhí)行一些特定任務(wù),比如常用for{…}for{select{…}}形式,還可以用定時(shí)器或事件驅(qū)動(dòng)執(zhí)行。下面是Go給每個(gè)P內(nèi)置的GC goroutine就是這種場(chǎng)景的。

// $GOROOT/src/runtime/mgc.go

func gcBgMarkStartWorkers() {

        // Background marking is performed by per-P G's. Ensure that

        // each P has a background GC G.

        for _, p := range allp {

                if p.gcBgMarkWorker == 0 {

                        go gcBgMarkWorker(p) // 這里每個(gè)P創(chuàng)建一個(gè)goroutine,以運(yùn)行gcBgMarkWorker

                        notetsleepg(&work.bgMarkReady, -1)

                        noteclear(&work.bgMarkReady)

                }

        }

}

func gcBgMarkWorker(_p_ *p) {

    gp := getg()

    ... ...

    for { 

            // 處理GC

        ... ...

    }

}

b) join模式

在線程模型中,父線程可以通過(guò)pthread join來(lái)等待子線程結(jié)束并獲取子線程的結(jié)束狀態(tài)。在Go中,我們有時(shí)候也有這種需求:goroutine的創(chuàng)建者需要等待新goroutine的結(jié)果。

type Worker struct {

}

func Do(f func()) chan Worker {

    w:= make(chan Worker)

    go func() {

        f()

        w<-Worker{}

    }()

    return w

}

func main() {

    c:=Do(func() {

        fmt.Print('到下班時(shí)間了...')

    })

    <-c

}

我們還是看剛剛上面的這個(gè)例子,Do函數(shù)使用典型的goroutine的創(chuàng)建模式創(chuàng)建了一個(gè)groutinemaingoroutine作為創(chuàng)建通過(guò)Do函數(shù)返回的channel與新goroutine建立關(guān)系,這個(gè)channel得用途就是在goroutine之間建立退出時(shí)間的信號(hào)通信機(jī)制。main goroutine在創(chuàng)建完新goroutine后就在該channel上阻塞等待了,直到新的goroutine退出前向該channel發(fā)送了一個(gè)信號(hào)。

運(yùn)行代碼,結(jié)果如下:

到下班時(shí)間了...

Process finished with exit code 0

獲取goroutine的退出狀態(tài)

如果新goroutine的創(chuàng)建者不僅僅要等待goroutine的退出,還要知道結(jié)束狀態(tài),我們可以通過(guò)自定義類(lèi)型的channel來(lái)實(shí)現(xiàn)這樣的需求。

func add(a,b int) int{

    return a+b

}

func Do(f func(a,b int) int,a,b int) chan int{

    c:=make(chan int)

    go func() {

        r:=f(a,b)

        c<-r

    }()

    return c

}

func main() {

    c:=Do(add,1,5)

    fmt.Println(<-c)

}

運(yùn)行結(jié)果是 6

等待多個(gè)goroutine退出 

func add(a,b int) int{ 

    return a+b 

func Do(f func(a,b int) int,a,b,n int) chan int{ 

    c:=make(chan int) 

    var wg sync.WaitGroup 

    for i:=0;i<n;i++{ 

        wg.Add(1) 

        go func() { 

            r:=f(a,b) 

            fmt.Println(r) 

            wg.Done() 

        }() 

    } 

    go func() { 

        wg.Wait() 

        c<-100 

    }() 

    go func() { 

    }() 

    return c 

func main() { 

    c:=Do(add,1,5,5) 

    fmt.Println(<-c) 

運(yùn)行結(jié)果 

100 

c) notify-wait模式 

前面的場(chǎng)景中,goroutine的創(chuàng)建者都是在被動(dòng)地等待新goroutine的退出。有些場(chǎng)景,goroutine的創(chuàng)建者需要主動(dòng)通知那些新goroutine退出。 

通知并等待一個(gè)goroutine的退出 

func add(a, b int) int { 

    return a + b 

func Do(f func(a, b int) int, a, b int) chan int { 

    quit := make(chan int) 

    go func() { 

        var job chan string 

        for { 

            select { 

            case x := <-job: 

                f(a, b) 

                fmt.Println(x) 

            case y := <-quit: 

                quit <- y 

            } 

        } 

    }() 

    return quit 

func main() { 

    c := Do(add, 1, 5) 

    fmt.Println('開(kāi)始干活') 

    time.Sleep(1 * time.Second) 

    c <- 0 

    timer := time.NewTimer(time.Second * 10) 

    defer timer.Stop() 

    select { 

    case status := <-c: 

        fmt.Println(status) 

    case <-timer.C: 

        fmt.Println('等待...') 

    } 

執(zhí)行代碼結(jié)果如下 

開(kāi)始干活 

通知并等待多個(gè)goroutine退出 

下面是通知并等待多個(gè)goroutine退出的場(chǎng)景。Go語(yǔ)言的channel有一個(gè)特性,那就是當(dāng)使用close函數(shù)關(guān)閉channel時(shí),所有阻塞到該channel上的goroutine都會(huì)得到通知。 

func worker(x int)  { 

    time.Sleep(time.Second * time.Duration(x)) 

func Do(f func(a int), n int) chan int { 

    quit := make(chan int) 

    job:=make(chan int) 

    var wg sync.WaitGroup 

    for i:=0;i<n;i++ { 

        wg.Add(1) 

        go func(i int) { 

            defer wg.Done() 

            name := fmt.Sprintf('worker-%d',i) 

            for { 

                j,ok:=<-job 

                if !ok{ 

                    fmt.Println(name,'done') 

                    return 

                } 

                worker(j) 

            } 

        }(i) 

    } 

    go func() { 

        <-quit 

        close(job) 

        wg.Wait() 

        quit<-200 

    }() 

    return quit 

func main() { 

    quit:=Do(worker,5) 

    fmt.Println('func Work...') 

    quit<-1 

    timer := time.NewTimer(time.Second * 10) 

    defer timer.Stop() 

    select { 

    case status := <-quit: 

        fmt.Println(status) 

    case <-timer.C: 

        fmt.Println('等待...') 

    } 

運(yùn)行結(jié)果 

func Work... 

worker-1 done 

worker-2 done 

worker-3 done 

worker-4 done 

worker-0 done 

200 

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多