最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Go協(xié)程和channel的實(shí)現(xiàn)

 更新時(shí)間:2026年01月28日 11:26:22   作者:JetTsang  
本文主要介紹了GO利用channel協(xié)調(diào)協(xié)程的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

一:?? 協(xié)程

Goroutine 是 Go 運(yùn)行時(shí)管理的輕量級(jí)線程

在 go 中,開啟一個(gè)協(xié)程是非常簡單的

package main

import (
  "fmt"
  "time"
)

func sing() {
  fmt.Println("唱歌")
  time.Sleep(1 * time.Second)
  fmt.Println("唱歌結(jié)束")
}

func main() {
  go sing()
  go sing()
  go sing()
  go sing()
  time.Sleep(2 * time.Second)
}

如果我把這個(gè)主線程中的延時(shí)去掉之后,你會(huì)發(fā)現(xiàn)程序沒有任何輸出就結(jié)束了

這是為什么呢

那是因?yàn)橹骶€程結(jié)束協(xié)程自動(dòng)結(jié)束,主線程不會(huì)等待協(xié)程的結(jié)束

?? WaitGroup

我們只需要讓主線程等待協(xié)程就可以了,它的用法是這樣的

package main

import (
  "fmt"
  "sync"
  "time"
)

var (
  wait = sync.WaitGroup{}
)

func sing() {
  fmt.Println("唱歌")
  time.Sleep(1 * time.Second)
  fmt.Println("唱歌結(jié)束")
  wait.Done()
}

func main() {
  wait.Add(4)
  go sing()
  go sing()
  go sing()
  go sing()
  wait.Wait()
  fmt.Println("主線程結(jié)束")
}

二:?? channel

有沒有想過一個(gè)問題,我在協(xié)程里面產(chǎn)生了數(shù)據(jù),咋傳遞給主線程呢?

或者是怎么傳遞給其他協(xié)程函數(shù)呢?

這個(gè)時(shí)候 channel 來了

基本定義

package main

import "fmt"

func main() {
  var c chan int // 聲明一個(gè)傳遞整形的通道
  // 初始化通道
  c = make(chan int, 1) //  初始化一個(gè) 有一個(gè)緩沖位的通道
  c <- 1
  //c <- 2 // 會(huì)報(bào)錯(cuò) deadlock
  fmt.Println(<-c) // 取值
  //fmt.Println(<-c) // 再取也會(huì)報(bào)錯(cuò)  deadlock

  c <- 2
  n, ok := <-c
  fmt.Println(n, ok)
  close(c) // 關(guān)閉協(xié)程
  c <- 3   // 關(guān)閉之后就不能再寫或讀了  send on closed channel
  fmt.Println(c)
}

當(dāng)然,在同步模式下,channel 沒有任何意義

需要在異步模式下使用 channel,在協(xié)程函數(shù)里面寫,在主線程里面接收數(shù)據(jù)

package main

import (
  "fmt"
  "sync"
  "time"
)

var moneyChan = make(chan int) // 聲明并初始化一個(gè)長度為0的信道

func pay(name string, money int, wait *sync.WaitGroup) {
  fmt.Printf("%s 開始購物\n", name)
  time.Sleep(1 * time.Second)
  fmt.Printf("%s 購物結(jié)束\n", name)

  moneyChan <- money

  wait.Done()
}

// 協(xié)程
func main() {
  var wait sync.WaitGroup
  startTime := time.Now()
  // 現(xiàn)在的模式,就是購物接力
  //shopping("張三")
  //shopping("王五")
  //shopping("李四")
  wait.Add(3)
  // 主線程結(jié)束,協(xié)程函數(shù)跟著結(jié)束
  go pay("張三", 2, &wait)
  go pay("王五", 3, &wait)
  go pay("李四", 5, &wait)

  go func() {
    defer close(moneyChan)
    // 在協(xié)程函數(shù)里面等待上面三個(gè)協(xié)程函數(shù)結(jié)束
    wait.Wait()
  }()

  for {
    money, ok := <-moneyChan
    fmt.Println(money, ok)
    if !ok {
      break
    }
  }

  //time.Sleep(2 * time.Second)

  fmt.Println("購買完成", time.Since(startTime))
  fmt.Println("moneyList", moneyList)
}

如果這樣接收數(shù)據(jù)不太優(yōu)雅,那還有更優(yōu)雅的寫法

  for money := range moneyChan {
    moneyList = append(moneyList, money)
  }

如果通道被 close,for 循環(huán)會(huì)自己結(jié)束,十分優(yōu)雅

?? select

如果一個(gè)協(xié)程函數(shù),往多個(gè) channel 里面寫東西,在主線程里面怎么拿數(shù)據(jù)呢?

go 為我們提供了 select,用于異步的從多個(gè) channel 里面去取數(shù)據(jù)

package main

import (
	"fmt"
	"sync"
	"time"
)

// 信道 存 int 類型
var moneyChan1 = make(chan int) // 聲明并初始化一個(gè)長度為0的信道
var nameChan1 = make(chan string)
var doneChan = make(chan struct{})

func send(name string, money int, wait *sync.WaitGroup) {
	fmt.Printf("%s 開始購物\n", name)
	time.Sleep(1 * time.Second)
	fmt.Printf("%s 購物結(jié)束\n", name)

	moneyChan1 <- money // 信道賦值語句
	nameChan1 <- name

	wait.Done()
}

func main() {
	var wait sync.WaitGroup

	startTime := time.Now()
	wait.Add(3)
	// 協(xié)程
	go send("zhangsan", 2, &wait)
	go send("lisi", 3, &wait)
	go send("wangwu", 5, &wait)

	// 再開一個(gè)協(xié)程函數(shù)判斷是否結(jié)束
	go func() {
		defer close(moneyChan1)
		defer close(nameChan1)
		defer close(doneChan)
		wait.Wait()
		// 再創(chuàng)建一個(gè)信道用于關(guān)閉
		// close(moneyChan)
	}()

	// 等價(jià)于下面的寫法
	var moneyList []int
	var nameList []string

	// 多個(gè) channel 的寫法
	var event = func() {
		for {
			select {
			case money := <-moneyChan1:
				moneyList = append(moneyList, money)
			case name := <-nameChan1:
				nameList = append(nameList, name)
			case <-doneChan:
				return
			}
		}
	}
	event()

	fmt.Println("購買完成", time.Since(startTime))
	fmt.Println("moneyList", moneyList)
	fmt.Println("nameList", nameList)
}

?? 協(xié)程超時(shí)處理

package main

import (
  "fmt"
  "time"
)

var done = make(chan struct{})

func event() {
  fmt.Println("event執(zhí)行開始")
  time.Sleep(2 * time.Second)
  fmt.Println("event執(zhí)行結(jié)束")
  close(done)
}

func main() {
  go event()

  select {
  case <-done:
    fmt.Println("協(xié)程執(zhí)行完畢")
  case <-time.After(1 * time.Second):
    fmt.Println("超時(shí)")
    return
  }

}

三:?? 線程安全

什么是線程安全?

現(xiàn)在有兩個(gè)協(xié)程,同時(shí)觸發(fā),一個(gè)協(xié)程對(duì)一個(gè)全局變量進(jìn)行 100 完成 ++ 操作,另一個(gè)對(duì)全局變量—的操作

那么,兩個(gè)協(xié)程結(jié)束,最后的值應(yīng)該是0才對(duì)

package main

import (
  "fmt"
  "sync"
)

var num int
var wait sync.WaitGroup

func add() {
  for i := 0; i < 1000000; i++ {
    num++
  }
  wait.Done()
}
func reduce() {
  for i := 0; i < 1000000; i++ {
    num--
  }
  wait.Done()
}

func main() {
  wait.Add(2)
  go add()
  go reduce()
  wait.Wait()
  fmt.Println(num)

}

但是你會(huì)發(fā)現(xiàn),這個(gè)輸出的結(jié)果完全無法預(yù)測(cè)

這是為什么呢?

根本原因是 CPU 的調(diào)度方法為搶占式執(zhí)行,隨機(jī)調(diào)度

?? 同步鎖

那么我們能不能通過給操作加鎖來解決這個(gè)問題呢

答案是可以的

package main

import (
  "fmt"
  "sync"
)

var num int
var wait  sync.WaitGroup
var lock  sync.Mutex

func add() {
  // 誰先搶到了這把鎖,誰就把它鎖上,一旦鎖上,其他的線程就只能等著
  lock.Lock()
  for i := 0; i < 1000000; i++ {
    num++
  }
  lock.Unlock()
  wait.Done()
}
func reduce() {
  lock.Lock()
  for i := 0; i < 1000000; i++ {
    num--
  }
  lock.Unlock()
  wait.Done()
}

func main() {
  wait.Add(2)
  go add()
  go reduce()
  wait.Wait()
  fmt.Println(num)

}

?? 線程安全下的 map

如果我們?cè)谝粋€(gè)協(xié)程函數(shù)下,讀寫 map 就會(huì)引發(fā)一個(gè)錯(cuò)誤

concurrent map read and map write

希望大家見到這個(gè)錯(cuò)誤,就能知道,這個(gè)就是 map 的線程安全錯(cuò)誤

package main

import (
  "fmt"
  "sync"
  "time"
)

var wait sync.WaitGroup
var mp = map[string]string{}

func reader() {
  for {
    fmt.Println(mp["time"])
  }
  wait.Done()
}
func writer() {
  for {
    mp["time"] = time.Now().Format("15:04:05")
  }
  wait.Done()
}

func main() {
  wait.Add(2)
  go reader()
  go writer()
  wait.Wait()

}

我們不能在并發(fā)模式下讀寫 map

如果要這樣做

  1. 給讀寫操作加鎖
  2. 使用sync.Map

加鎖

package main

import (
  "fmt"
  "sync"
  "time"
)

var wait sync.WaitGroup
var mp = map[string]string{}
var lock sync.Mutex

func reader() {
  for {
    lock.Lock()
    fmt.Println(mp["time"])
    lock.Unlock()
  }
  wait.Done()
}
func writer() {
  for {
    lock.Lock()
    mp["time"] = time.Now().Format("15:04:05")
    lock.Unlock()
  }
  wait.Done()
}

func main() {
  wait.Add(2)
  go reader()
  go writer()
  wait.Wait()
}

sync.Map

package main

import (
  "fmt"
  "sync"
  "time"
)

var wait sync.WaitGroup
var mp = sync.Map{}

func reader() {
  for {

    fmt.Println(mp.Load("time"))
  }
  wait.Done()
}
func writer() {
  for {
    mp.Store("time", time.Now().Format("15:04:05"))
  }
  wait.Done()
}

func main() {
  wait.Add(2)
  go reader()
  go writer()
  wait.Wait()

}

其實(shí)看它源碼,它的內(nèi)部也是用了同步鎖的

到此這篇關(guān)于Go協(xié)程和channel的實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)Go協(xié)程和channel內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Golang中字符串(string)與字節(jié)數(shù)組([]byte)一行代碼互轉(zhuǎn)實(shí)例

    Golang中字符串(string)與字節(jié)數(shù)組([]byte)一行代碼互轉(zhuǎn)實(shí)例

    golang語言本身就是c的工具集,開發(fā)c的程序用到的大部分結(jié)構(gòu)體,內(nèi)存管理,攜程等,golang基本都有,下面這篇文章主要給大家介紹了關(guān)于Golang中字符串(string)與字節(jié)數(shù)組([]byte)一行代碼互轉(zhuǎn)的相關(guān)資料,需要的朋友可以參考下
    2022-09-09
  • Golang 之協(xié)程的用法講解

    Golang 之協(xié)程的用法講解

    這篇文章主要介紹了Golang 之協(xié)程的用法講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • Go使用WebSocket實(shí)現(xiàn)一個(gè)公域聊天室

    Go使用WebSocket實(shí)現(xiàn)一個(gè)公域聊天室

    公域聊天室是一種實(shí)時(shí)通信服務(wù),所有用戶連接到同一個(gè)公共房間,本文下面就介紹一下Go使用WebSocket實(shí)現(xiàn)一個(gè)公域聊天室,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2026-03-03
  • Go實(shí)現(xiàn)簡單的數(shù)據(jù)庫表轉(zhuǎn)結(jié)構(gòu)體詳解

    Go實(shí)現(xiàn)簡單的數(shù)據(jù)庫表轉(zhuǎn)結(jié)構(gòu)體詳解

    這篇文章主要為大家介紹了Go實(shí)現(xiàn)簡單的數(shù)據(jù)庫表轉(zhuǎn)結(jié)構(gòu)體詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-01-01
  • gin?框架下使用URL重定向

    gin?框架下使用URL重定向

    本文主要介紹了gin?框架下使用URL重定向,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2026-05-05
  • 詳解Golang如何優(yōu)雅判斷interface是否為nil

    詳解Golang如何優(yōu)雅判斷interface是否為nil

    這篇文章主要為大家詳細(xì)介紹了Golang如何優(yōu)雅判斷interface是否為nil的相關(guān)知識(shí),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解下
    2024-01-01
  • go語言int64整型轉(zhuǎn)字符串的實(shí)現(xiàn)

    go語言int64整型轉(zhuǎn)字符串的實(shí)現(xiàn)

    本文主要介紹了go語言int64整型轉(zhuǎn)字符串的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-03-03
  • golang的序列化與反序列化的幾種方式

    golang的序列化與反序列化的幾種方式

    這篇文章主要介紹了golang的序列化與反序列化的幾種方式,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-10-10
  • 手把手帶你走進(jìn)Go語言之類型轉(zhuǎn)換

    手把手帶你走進(jìn)Go語言之類型轉(zhuǎn)換

    每個(gè)函數(shù)都可以強(qiáng)制將一個(gè)表達(dá)式轉(zhuǎn)換成某種特定數(shù)據(jù)類型,本文給大家介紹了在Go語言中類型轉(zhuǎn)換的具體用法,講述的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值
    2021-09-09
  • go如何使用cobra啟動(dòng)項(xiàng)目

    go如何使用cobra啟動(dòng)項(xiàng)目

    本文介紹了如何使用Go語言的Cobra庫來啟動(dòng)項(xiàng)目,包括基本使用和項(xiàng)目開發(fā)中的具體實(shí)現(xiàn),本文結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧
    2025-11-11

最新評(píng)論

汉沽区| 建平县| 阳春市| 东丽区| 黔西县| 嘉黎县| 曲水县| 普格县| 丁青县| 县级市| 平遥县| 泸定县| 新兴县| 吉林市| 亚东县| 赤城县| 城口县| 太仆寺旗| 清新县| 辽源市| 汉源县| 沙坪坝区| 临漳县| 鄄城县| 潼关县| 滦南县| 新蔡县| 大足县| 利川市| 鹤庆县| 新和县| 稷山县| 常州市| 内黄县| 浦北县| 银川市| 陇西县| 新疆| 靖江市| 泰兴市| 滦南县|