欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

圖文詳解Go中的channel

 更新時間:2023年02月27日 09:31:10   作者:始夢的少年  
Channel是go語言內(nèi)置的一個非常重要的特性,也是go并發(fā)編程的兩大基石之一,下面這篇文章主要給大家介紹了關(guān)于Go中channel的相關(guān)資料,需要的朋友可以參考下

channel

Go語言中的通道(channel)是一種特殊的類型。

在任何時候,同時只能有一個 goroutine 訪問通道進行發(fā)送和獲取數(shù)據(jù)。goroutine 間通過通道就可以通信。

通道像一個傳送帶或者隊列,總是遵循先入先出(First In First Out)的規(guī)則,保證收發(fā)數(shù)據(jù)的順序。

(1)channel本身是一個隊列,先進先出

(2)線程安全,不需要加鎖

(3)本身是有類型的,string, int 等,如果要存多種類型,則定義成 interface類型

(4)channel是引用類型,必須make之后才能使用,一旦 make,它的容量就確定了,不會動態(tài)增加?。∷蚼ap,slice不一樣

特點:

(1)一旦初始化容量,就不會改變了。

(2)當(dāng)寫滿時,不可以寫,取空時,不可以取。

(3)發(fā)送將持續(xù)阻塞直到數(shù)據(jù)被接收

把數(shù)據(jù)往通道中發(fā)送時,如果接收方一直都沒有接收,那么發(fā)送操作將持續(xù)阻塞。Go 程序運行時能智能地發(fā)現(xiàn)一些永遠無法發(fā)送成功的語句并做出提示

(4)接收將持續(xù)阻塞直到發(fā)送方發(fā)送數(shù)據(jù)。

如果接收方接收時,通道中沒有發(fā)送方發(fā)送數(shù)據(jù),接收方也會發(fā)生阻塞,直到發(fā)送方發(fā)送數(shù)據(jù)為止。

(5)每次接收一個元素。

通道一次只能接收一個數(shù)據(jù)元素。

1、關(guān)于 channel的聲明和使用的代碼:

package main
import (
	"fmt"
)

func main() {

	//演示一下管道的使用
	//1. 創(chuàng)建一個可以存放3個int類型的管道
	var intChan chan int
	intChan = make(chan int, 3)

	//2. 看看intChan是什么
	fmt.Printf("intChan 的值=%v intChan本身的地址=%p\n", intChan, &intChan)


	//3. 向管道寫入數(shù)據(jù)
	intChan<- 10
	num := 211
	intChan<- num
	intChan<- 50
	// //如果從channel取出數(shù)據(jù)后,可以繼續(xù)放入
	<-intChan
	intChan<- 98//注意點, 當(dāng)我們給管寫入數(shù)據(jù)時,不能超過其容量


	//4. 看看管道的長度和cap(容量)
	fmt.Printf("channel len= %v cap=%v \n", len(intChan), cap(intChan)) // 3, 3

	//5. 從管道中讀取數(shù)據(jù)

	var num2 int
	num2 = <-intChan 
	fmt.Println("num2=", num2)
	fmt.Printf("channel len= %v cap=%v \n", len(intChan), cap(intChan))  // 2, 3

	//6. 在沒有使用協(xié)程的情況下,如果我們的管道數(shù)據(jù)已經(jīng)全部取出,再取就會報告 deadlock

	num3 := <-intChan
	num4 := <-intChan

	//num5 := <-intChan

	fmt.Println("num3=", num3, "num4=", num4/*, "num5=", num5*/)
}
fmt.Printf("intChan 的值=%v intChan本身的地址=%p\n", intChan, &intChan)

這句代碼顯示:channel其實和指針一樣,本身存放在一個內(nèi)存單元中,有它的地址,而它的值是一個 int類型的地址。

2、注意空接口類型的 channel

package main
import (
	"fmt"
)

type Cat struct {
	Name string
	Age int
}

func main() {

	//定義一個存放任意數(shù)據(jù)類型的管道 3個數(shù)據(jù)
	//var allChan chan interface{}
	allChan := make(chan interface{}, 3)

	allChan<- 10
	allChan<- "tom jack"
	cat := Cat{"小花貓", 4}
	allChan<- cat

	//我們希望獲得到管道中的第三個元素,則先將前2個推出
	<-allChan
	<-allChan

	newCat := <-allChan //從管道中取出的Cat是什么?

	fmt.Printf("newCat=%T , newCat=%v\n", newCat, newCat)
	//下面的寫法是錯誤的!編譯不通過
	//fmt.Printf("newCat.Name=%v", newCat.Name)
	//使用類型斷言
	a := newCat.(Cat) 
	fmt.Printf("newCat.Name=%v", a.Name)
}

定義 interface類型的空接口,可以接收任意類型的數(shù)據(jù),但是在取出來的時候,必須斷言!
a := newCat.(Cat)

3、channel的關(guān)閉:close( )

關(guān)閉之后,不能再寫入,只能讀。

只能由發(fā)送者執(zhí)行這句代碼

4、channel的遍歷: for … range

通道的數(shù)據(jù)接收一共有以下 4 種寫法。

1.阻塞接收數(shù)據(jù)

阻塞模式接收數(shù)據(jù)時,將接收變量作為<-操作符的左值,格式如下:

data := <-ch

執(zhí)行該語句時將會阻塞,直到接收到數(shù)據(jù)并賦值給 data 變量。

2.非阻塞接收數(shù)據(jù)(有問題啊,還是會報錯deadlock

使用非阻塞方式從通道接收數(shù)據(jù)時,語句不會發(fā)生阻塞,格式如下:

data, ok := <-ch

data:表示接收到的數(shù)據(jù)。未接收到數(shù)據(jù)時,data 為通道類型的零值。
ok:表示是否接收到數(shù)據(jù)。

非阻塞的通道接收方法可能造成高的 CPU 占用,因此使用非常少。如果需要實現(xiàn)接收超時檢測,可以配合 select 和計時器 channel進行,可以參見后面的內(nèi)容。

3.接收任意數(shù)據(jù),忽略接收的數(shù)據(jù)

阻塞接收數(shù)據(jù)后,忽略從通道返回的數(shù)據(jù),格式如下:

<-ch

執(zhí)行該語句時將會發(fā)生阻塞,直到接收到數(shù)據(jù),但接收到的數(shù)據(jù)會被忽略。這個方式實際上只是通過通道在 goroutine 間阻塞收發(fā)實現(xiàn)并發(fā)同步。

使用通道做并發(fā)同步的寫法,可以參考下面的例子:

package main

import (
    "fmt"
)

func main() {

    // 構(gòu)建一個通道
    ch := make(chan int)

    // 開啟一個并發(fā)匿名函數(shù)
    go func() {

        fmt.Println("start goroutine")

        // 通過通道通知main的goroutine
        ch <- 0

        fmt.Println("exit goroutine")

    }()

    fmt.Println("wait goroutine")

    // 等待匿名goroutine
    <-ch

    fmt.Println("all done")

}

4.循環(huán)接收

通道的數(shù)據(jù)接收可以借用 for range 語句進行多個元素的接收操作,格式如下:

for data := range ch {
}

通道 ch 是可以進行遍歷的,遍歷的結(jié)果就是接收到的數(shù)據(jù)。數(shù)據(jù)類型就是通道的數(shù)據(jù)類型。通過 for 遍歷獲得的變量只有一個,即上面例子中的 data。

package main
import (
	"fmt"
)

func main() {

	intChan := make(chan int, 3)
	intChan<- 100
	intChan<- 200
	close(intChan) // close
	//這時不能夠再寫入數(shù)到channel
	//intChan<- 300
	fmt.Println("okook~")
	//當(dāng)管道關(guān)閉后,讀取數(shù)據(jù)是可以的
	n1 := <-intChan
	fmt.Println("n1=", n1)


	//遍歷管道
	intChan2 := make(chan int, 100)
	for i := 0; i < 100; i++ {
		intChan2<- i * 2  //放入100個數(shù)據(jù)到管道
	}

	//遍歷管道不能使用普通的 for 循環(huán)
	// for i := 0; i < len(intChan2); i++ {

	// }
	//在遍歷時,如果channel沒有關(guān)閉,則會出現(xiàn)deadlock的錯誤
	//在遍歷時,如果channel已經(jīng)關(guān)閉,則會正常遍歷數(shù)據(jù),遍歷完后,就會退出遍歷
	close(intChan2)
	for v := range intChan2 { //沒有下標
		fmt.Println("v=", v)
	}
}

在遍歷管道之前要先關(guān)閉管道,不然會出現(xiàn)deadlock的錯誤

應(yīng)用1

開兩個管道;

當(dāng)寫協(xié)程完成工作之后,close數(shù)據(jù)管道,讀協(xié)程對數(shù)據(jù)管道 intChan的數(shù)據(jù)讀完之后,就向退出管道 exitChan 寫入一個 true,close掉;

主線程循環(huán)檢測退出管道里是否有數(shù)據(jù),如果有,說明讀協(xié)程完成,主程序就可以退出了。

package main

import (
	"fmt"
)

//write Data
func writeData(intChan chan int) {
	for i := 1; i <= 50; i++ {
		//放入數(shù)據(jù)
		intChan <- i //
		fmt.Println("writeData ", i)
		//time.Sleep(time.Second)
	}
	close(intChan) //關(guān)閉
}

//read data
func readData(intChan chan int, exitChan chan bool) {

	for {
		v, ok := <-intChan
		if !ok {
			break
		}
		// time.Sleep(time.Second)
		fmt.Printf("readData 讀到數(shù)據(jù)=%v\n", v)
	}
	//readData 讀取完數(shù)據(jù)后,即任務(wù)完成
	exitChan <- true
	close(exitChan)

}

func main() {

	//創(chuàng)建兩個管道
	intChan := make(chan int, 10)
	exitChan := make(chan bool, 1)

	go writeData(intChan)
	go readData(intChan, exitChan)

	//time.Sleep(time.Second * 10)
	for {
		_, ok := <-exitChan
		if !ok {
			break
		}
	}

}

應(yīng)用2

定義三個管道:

  • intChan :放8000個數(shù)
  • primeChan:放素數(shù)
  • exitChan :4個協(xié)程運行完畢的標志
package main

import (
	"fmt"
	"time"
)

//向 intChan放入 1-8000個數(shù)
func putNum(intChan chan int) {

	for i := 1; i <= 80000; i++ {
		intChan <- i
	}

	//關(guān)閉intChan
	close(intChan)
}

// 從 intChan取出數(shù)據(jù),并判斷是否為素數(shù),如果是,就
// 	//放入到primeChan
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {

	//使用for 循環(huán)
	// var num int
	var flag bool //
	for {
		//time.Sleep(time.Millisecond * 10)
		num, ok := <-intChan //intChan 取不到..

		if !ok {
			break
		}
		flag = true //假設(shè)是素數(shù)
		//判斷num是不是素數(shù)
		for i := 2; i < num; i++ {
			if num%i == 0 { //說明該num不是素數(shù)
				flag = false
				break
			}
		}

		if flag {
			//將這個數(shù)就放入到primeChan
			primeChan <- num
		}
	}

	fmt.Println("有一個primeNum 協(xié)程因為取不到數(shù)據(jù),退出")
	//這里我們還不能關(guān)閉 primeChan
	//向 exitChan 寫入true
	exitChan <- true

}

func main() {

	intChan := make(chan int, 1000)
	primeChan := make(chan int, 20000) //放入結(jié)果
	//標識退出的管道
	exitChan := make(chan bool, 4) // 4個

	start := time.Now().Unix()

	//開啟一個協(xié)程,向 intChan放入 1-8000個數(shù)
	go putNum(intChan)
	//開啟4個協(xié)程,從 intChan取出數(shù)據(jù),并判斷是否為素數(shù),如果是,就
	//放入到primeChan
	for i := 0; i < 4; i++ {
		go primeNum(intChan, primeChan, exitChan)
	}

	//這里我們主線程,進行處理
	//直接
	go func() {
		for i := 0; i < 4; i++ {
			<-exitChan
		}

		end := time.Now().Unix()
		fmt.Println("使用協(xié)程耗時=", end-start)

		//當(dāng)我們從exitChan 取出了4個結(jié)果,就可以放心的關(guān)閉 prprimeChan
		close(primeChan)
	}()

	//遍歷我們的 primeChan ,把結(jié)果取出
	for {
		res, ok := <-primeChan
		if !ok {
			break
		}
		//將結(jié)果輸出
		fmt.Printf("素數(shù)=%d\n", res)
	}

	fmt.Println("main線程退出")

}

有一個primeNum 協(xié)程因為取不到數(shù)據(jù),退出
有一個primeNum 協(xié)程因為取不到數(shù)據(jù),退出
有一個primeNum 協(xié)程因為取不到數(shù)據(jù),退出
有一個primeNum 協(xié)程因為取不到數(shù)據(jù),退出
使用協(xié)程耗時= 3
main線程退出

存數(shù)字和計算素數(shù)比較簡單,不提

開啟4個協(xié)程,運算素數(shù),效率比單個線程高幾倍!

go func() {
		for i := 0; i < 4; i++ {
			<-exitChan
		}

		end := time.Now().Unix()
		fmt.Println("使用協(xié)程耗時=", end-start)

		//當(dāng)我們從exitChan 取出了4個結(jié)果,就可以放心的關(guān)閉 prprimeChan
		close(primeChan)
	}()

這里定義了一個匿名協(xié)程,作用是檢測4個協(xié)程 有沒有完成運行,取不出來就會阻塞,等待協(xié)程完成。也可以這樣:
if len(exitChan) == 4

總結(jié)

到此這篇關(guān)于圖文詳解Go中的channel的文章就介紹到這了,更多相關(guān)Go中channel內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • golang多維度排序及題解最長連續(xù)序列

    golang多維度排序及題解最長連續(xù)序列

    這篇文章主要為大家介紹了golang多維度排序及題解最長連續(xù)序列示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-10-10
  • Go?Ticker?周期性定時器用法及實現(xiàn)原理詳解

    Go?Ticker?周期性定時器用法及實現(xiàn)原理詳解

    這篇文章主要為大家介紹了Go?Ticker?周期性定時器用法及實現(xiàn)原理詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • Go語言中interface語法與使用詳解

    Go語言中interface語法與使用詳解

    Go語言里面設(shè)計最精妙的應(yīng)該算interface,它讓面向?qū)ο?內(nèi)容組織實現(xiàn)非常的方便,下面這篇文章主要給大家介紹了關(guān)于Go語言中interface語法與使用的相關(guān)資料,需要的朋友可以參考下
    2022-07-07
  • Go 1.21新增的slices包中切片函數(shù)用法詳解

    Go 1.21新增的slices包中切片函數(shù)用法詳解

    Go 1.21新增的 slices 包提供了很多和切片相關(guān)的函數(shù),可以用于任何類型的切片,本文通過代碼示例為大家介紹了部分切片函數(shù)的具體用法,感興趣的小伙伴可以了解一下
    2023-08-08
  • golang 的string與[]byte轉(zhuǎn)換方式

    golang 的string與[]byte轉(zhuǎn)換方式

    這篇文章主要介紹了golang 的string與[]byte轉(zhuǎn)換方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 淺談golang結(jié)構(gòu)體偷懶初始化

    淺談golang結(jié)構(gòu)體偷懶初始化

    這篇文章主要介紹了淺談golang結(jié)構(gòu)體偷懶初始化,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-12-12
  • Go unsafe 包的使用詳解

    Go unsafe 包的使用詳解

    這篇文章主要介紹了Go unsafe 包的使用詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-01-01
  • Golang中Model的具體使用

    Golang中Model的具體使用

    本文主要介紹了Golang中Model的具體使用,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2023-04-04
  • Goland使用delve進行遠程調(diào)試的詳細教程

    Goland使用delve進行遠程調(diào)試的詳細教程

    網(wǎng)上給出的使用delve進行遠程調(diào)試,都需要先在本地交叉編譯或者在遠程主機上編譯出可運行的程序,然后再用delve在遠程啟動程序,本教程會將上面的步驟簡化為只需要兩步,1,在遠程運行程序2,在本地啟動調(diào)試,需要的朋友可以參考下
    2024-08-08
  • Go語言學(xué)習(xí)筆記之錯誤和異常詳解

    Go語言學(xué)習(xí)筆記之錯誤和異常詳解

    Go語言采用返回值的形式來返回錯誤,這一機制既可以讓開發(fā)者真正理解錯誤處理的含義,也可以大大降低程序的復(fù)雜度,下面這篇文章主要給大家介紹了關(guān)于Go語言學(xué)習(xí)筆記之錯誤和異常的相關(guān)資料,需要的朋友可以參考下
    2022-07-07

最新評論