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

Golang中g(shù)oroutine和channel使用介紹深入分析

 更新時(shí)間:2023年01月13日 14:44:23   作者:2019ab  
一次只做一件事情并不是完成任務(wù)最快的方法,一些大的任務(wù)可以拆解成若干個(gè)小任務(wù),goroutine可以讓程序同時(shí)處理幾個(gè)不同的任務(wù),goroutine使用channel來協(xié)調(diào)它們的工作,channel允許goroutine互相發(fā)送數(shù)據(jù)并同步,這樣一個(gè)goroutine就不會(huì)領(lǐng)先于另一個(gè)goroutine

1.goroutine-看一個(gè)需求

需求:要求統(tǒng)計(jì)1-900000000的數(shù)字中,那些是素?cái)?shù)?

分析:

  • 傳統(tǒng)方法,就是使用一個(gè)循環(huán),循環(huán)的判斷各個(gè)數(shù)是不是素?cái)?shù)。
  • 使用并發(fā)或并行的方式,將統(tǒng)計(jì)素?cái)?shù)的任務(wù)分配給多個(gè)goroutine去完成,這時(shí)就會(huì)使用到goroutine。

2.進(jìn)程和線程介紹

  • 進(jìn)程就是程序在操作系統(tǒng)中的一次執(zhí)行過程,是系統(tǒng)進(jìn)行資源分配和調(diào)度的基本單位
  • 線程是進(jìn)程的一個(gè)執(zhí)行實(shí)例,是程序執(zhí)行的最小單位,它是比進(jìn)程更小的能獨(dú)立運(yùn)行的基本單位。
  • 一個(gè)進(jìn)程可以創(chuàng)建和銷毀多個(gè)線程,同一個(gè)進(jìn)程中的多個(gè)線程可以并發(fā)執(zhí)行
  • 一個(gè)程序至少有一個(gè)進(jìn)程,一個(gè)進(jìn)程至少有一個(gè)線程

3.并發(fā)和并行

  • 多線程程序在單核上運(yùn)行,就是并發(fā)
  • 多個(gè)程程序在多核上運(yùn)行,就是并行

并發(fā):因?yàn)槭窃谝粋€(gè)CPU上,比如有10個(gè)線程,每個(gè)線程執(zhí)行10毫秒(進(jìn)行輪詢操作),從人的角度看,好像這10個(gè)線程都在運(yùn)行,但是從微觀上看,在某一個(gè)時(shí)間點(diǎn)看,其實(shí)只有一個(gè)線程在執(zhí)行,這就是并發(fā)。

并行:因?yàn)槭窃诙鄠€(gè)CPU上(比如有10個(gè)CPU),比如有10個(gè)線程,每個(gè)線程執(zhí)行10毫秒(各自在不同CPU上執(zhí)行),從人的角度看,這10個(gè)線程都在運(yùn)行,但是從微觀上看,在某一個(gè)時(shí)間點(diǎn)看,也同時(shí)有10個(gè)線程在執(zhí)行,這就是并行

4.Go協(xié)程和Go主線程

Go主線程(有程序員直接稱為線程/也可以理解成進(jìn)程):一個(gè)Go線程上,可以起多個(gè)攜程,你可以這樣理解,攜程是輕量的線程

Go協(xié)程的特點(diǎn)

有獨(dú)立的棧空間

共享程序堆空間

調(diào)度由用戶控制

攜程是輕量級(jí)的線程

案例說明

請(qǐng)編寫一個(gè)程序,完成如下功能:

1.在主線程(可以理解成進(jìn)程)中,開啟一個(gè)goroutine,該攜程每隔1秒輸出“hello,world”

2.在主線程中也每隔一秒輸出“hello,golang”,輸出10次后,退出程序

3.要求主線程和goroutine同時(shí)執(zhí)行

4.畫出主線程和協(xié)程執(zhí)行流程圖

代碼實(shí)現(xiàn)

// 在主線程(可以理解成進(jìn)程)中,開啟一個(gè)goroutine,該協(xié)程每秒輸出 “hello,world”
// 在主線程中也每隔一秒輸出“hello,golang”,輸出10次后,退出程序
// 要求主線程和goroutine同時(shí)執(zhí)行
//編寫一個(gè)函數(shù),每隔1秒輸出 “hello,world”
func test(){
   for i := 1;i<=10;i++{
		fmt.Println("test() hello,world"+strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}
func main(){
    go test() // 開啟了一個(gè)協(xié)程
    for i:=1;i<=10;i++{
		fmt.Println(" main() hello,golang"+strconv.Itoa(i))
		time.Sleep(time.Second)
	}
}

總結(jié)

  • 主線程是一個(gè)物理線程,直接作用在CPU上的,是重量級(jí)的,非常耗費(fèi)CPU資源。
  • 協(xié)程從主線程開啟的,是輕量級(jí)的線程,是邏輯態(tài)。對(duì)資源消耗相對(duì)少。
  • Golang的協(xié)程機(jī)制是重要的特點(diǎn),可以輕松的開啟上萬個(gè)協(xié)程。其他編程語言的并發(fā)機(jī)制是一般基于線程的,開啟過多的線程,資源耗費(fèi)大,這里就突顯Golang在并發(fā)上的優(yōu)勢(shì)了

MPG模式基本介紹

M:操作系統(tǒng)的主線程(是物理線程)

P:協(xié)程執(zhí)行需要的上下文

G:協(xié)程

5.設(shè)置Golang運(yùn)行的CPU數(shù)

介紹:為了充分利用多CPU的優(yōu)勢(shì),在Golang程序中設(shè)置運(yùn)行的CPU數(shù)目

 package main
 import "fmt"
 import "runtime"
func main(){
	// 獲取當(dāng)前系統(tǒng)CPU的數(shù)量
	num := runtime.NumCPU()
	// 這里設(shè)置num-1的CPU運(yùn)行g(shù)o程序
	runtime.GOMAXPROCS(num)
	fmt.Println("num=",num)
}
  • go1.8后,默認(rèn)讓程序運(yùn)行在多個(gè)核上,可以不用設(shè)置了
  • go1.8前,還是要設(shè)置一下,可以更高效的利用CPU

6.channel(管道)看需求

需求:現(xiàn)在要計(jì)算 1-200的各個(gè)數(shù)的階乘,并且把各個(gè)數(shù)的階乘放入到map中。最后顯示出來。要求使用goroutine完成

分析思路:

使用goroutine來完成,效率高,但是會(huì)出現(xiàn)并發(fā)/并行安全問題

這里就提出了不同goroutine如何通信的問題

代碼實(shí)現(xiàn)

使用goroutine來完成(看看使用gorotine并發(fā)完成會(huì)出現(xiàn)什么問題?然后我們會(huì)去解決)

在運(yùn)行某個(gè)程序時(shí),如何知道是否存在資源競(jìng)爭(zhēng)問題,方法很簡單,在編譯該程序時(shí),增加一個(gè)參數(shù) -race即可

不同goroutine之間如何通訊

1.全局變量的互斥鎖

2.使用管道channel來解決

使用全局變量加鎖同步改進(jìn)程序

  • 英文沒有對(duì)全局變量m加鎖,因此會(huì)出現(xiàn)資源爭(zhēng)奪問題,代碼會(huì)出現(xiàn)錯(cuò)誤,提示concurrent map writes
  • 解決方案:加入互斥鎖
  • 我們的數(shù)的階乘很大,結(jié)果會(huì)越界,可以將求階乘改成sum += uint64(i)

源碼

package main
import (
	"fmt"
	"time"
	"sync"
)
// 需求:現(xiàn)在要計(jì)算 1-200的各個(gè)數(shù)的階乘,并且把各個(gè)數(shù)的階乘放入到map中
// 最后顯示出來。要求使用goroutine完成
// 思路
// 1. 編寫一個(gè)函數(shù),來計(jì)算各個(gè)數(shù)的階乘,并放入到map中
// 2. 我們啟動(dòng)的協(xié)程多個(gè),統(tǒng)計(jì)的將結(jié)果放入到map中
// 3. map應(yīng)該做出一個(gè)全局的
var (
  myMap = make(map[int]int,10)
  // 聲明一個(gè)全局的互斥鎖
  // lock 是一個(gè)全局的互斥鎖
  //sync 是包:synchornized 同步
  // Mutex: 是互斥
  lock sync.Mutex
)
// test函數(shù)就是計(jì)算n!,讓將這個(gè)結(jié)果放入到myMap
func test(n int){
	res := 1
	for i := 1;i<=n;i++{
		res *= i
	}
	// 這里我們將res放入到myMap
	// 加鎖
	lock.Lock()
	myMap[n] = res  // concurrent map writes?
	// 解鎖
	lock.Unlock()
}
func main(){
	// 我們這里開啟多個(gè)協(xié)程完成這個(gè)任務(wù)[200個(gè)]
	for i := 1;i<=20;i++{
		go test(i)
	}
	// 休眠10秒鐘【第二個(gè)問題】
	time.Sleep(time.Second * 10)
	lock.Lock()
	// 這里我們輸出結(jié)果 變量這個(gè)結(jié)果
	for i,v := range myMap{
		fmt.Printf("map[%d]=%d\n",i,v)
	} 
	lock.Unlock()
}

channel(管道)-基本使用

channel初始化

說明:使用make進(jìn)行初始化

var intChan chan int

intChan = make(chan int,10)

向channel中寫入(存放)數(shù)據(jù)

var intChan chan int

intChan = make(chan int,10)

num := 999

intChan <-10

intChan <-num

管道的初始化,寫入數(shù)據(jù)到管道,從管道讀取數(shù)據(jù)及基本的注意事項(xiàng)

package main
import (
	"fmt"
)
func main(){
	// 演示一下管道的使用
	// 1.創(chuàng)建一個(gè)可以存放3個(gè)int類型的管道
	var intChan chan int
	intChan = make(chan int,3)
	// 2.看看intChannel是什么
	fmt.Printf("intChan 的值=%v intChan本身的地址=%p\n",intChan,&intChan)
	// 3.向管道寫入數(shù)據(jù)
	intChan<- 10
	num := 211
	intChan<- num
	// 注意點(diǎn),當(dāng)我們給管寫入數(shù)據(jù)時(shí),不能超過其容量
	intChan<- 50
	// intChan<- 98
	//4. 看看管道的長度和cap(容量)
	fmt.Printf("channel len=%v cap=%v \n",len(intChan),cap(intChan)) // 2,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)全部取出,再取就會(huì)報(bào)告 deadlock
	num3 := <-intChan
	num4 := <-intChan
	// num5 := <-intChan
	fmt.Println("num3=",num3,"num4=",num4)//,"num5=",num5)
}

channel使用的注意事項(xiàng)

1.channel中只能存放指定的數(shù)據(jù)類型

2.channel的數(shù)據(jù)放滿后,就不能再放入了

3.如果從channel取出數(shù)據(jù)后,可以繼續(xù)放入

4. 在沒有使用協(xié)程的情況下,如果channel數(shù)據(jù)取完了,再取,就會(huì)報(bào)dead lock

示例代碼

package main
import (
	"fmt"
)
type Cat struct{
	Name string
	Age int
}
func main(){
	// 定義一個(gè)存放任意數(shù)據(jù)類型的管道  3個(gè)數(shù)據(jù)
	// var callChan chan interface{}
	allChan := make(chan interface{},3)
	allChan<- 10
	allChan<- "tom jack"
	cat := Cat{"小花貓",4}
	allChan<- cat
	// 我們希望獲得到管道中的第三個(gè)元素,則先將前2個(gè)推出
	<-allChan
	<-allChan
	newCat := <-allChan // 從管道中取出的Cat是什么?
	fmt.Printf("newCat=%T,newCat=%v\n",newCat,newCat)
	// 下面的寫法是錯(cuò)誤的!編譯不通過
	// fmt.Printf("newCat.Name=%v",newCat.Name)
	// 使用類型斷言
	a := newCat.(Cat)
	fmt.Printf("newCat.Name=%v",a.Name)
}

channel的關(guān)閉

使用內(nèi)置函數(shù)close可以關(guān)閉channel,當(dāng)channel關(guān)閉后,就不能再向channel寫數(shù)據(jù)了,但是仍然可以從該channel讀取數(shù)據(jù)

channel的遍歷

channel支持for-range的方式進(jìn)行遍歷,請(qǐng)注意兩個(gè)細(xì)節(jié)

  • 在遍歷時(shí),如果channel沒有關(guān)閉,則會(huì)出現(xiàn)deadlock的錯(cuò)誤
  • 在遍歷時(shí),如果channel已經(jīng)關(guān)閉,則會(huì)正常遍歷數(shù)據(jù),遍歷完后,就會(huì)退出遍歷。

代碼演示:

package main
import (
	"fmt"
)
func main(){
	intChan := make(chan int,3)
	intChan<- 100
	intChan<- 200
	close(intChan) // close
	// 這是不能夠再寫入到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個(gè)數(shù)據(jù)到管道
	}
	// 遍歷管道不能使用普通的for循環(huán)
	// 在遍歷時(shí),如果channel沒有關(guān)閉,則會(huì)出現(xiàn)deadlock的錯(cuò)誤
	// 在遍歷時(shí),如果channel已經(jīng)關(guān)閉,則會(huì)正常遍歷數(shù)據(jù),遍歷完后,就會(huì)退出遍歷
	close(intChan2)
	for  v := range intChan2{
		fmt.Println("v=",v)
	}
}

到此這篇關(guān)于Golang中g(shù)oroutine和channel使用介紹深入分析的文章就介紹到這了,更多相關(guān)Go goroutine與channel內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Goland激活碼破解永久版及安裝詳細(xì)教程(親測(cè)可以)

    Goland激活碼破解永久版及安裝詳細(xì)教程(親測(cè)可以)

    這篇文章主要介紹了Goland激活碼破解永久版及安裝詳細(xì)教程(親測(cè)可以),本文通過實(shí)例圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-10-10
  • Go語言清除文件中空行的方法

    Go語言清除文件中空行的方法

    這篇文章主要介紹了Go語言清除文件中空行的方法,實(shí)例分析了Go語言針對(duì)文件的操作技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下
    2015-02-02
  • Golang使用泛型對(duì)數(shù)組進(jìn)行去重的實(shí)現(xiàn)

    Golang使用泛型對(duì)數(shù)組進(jìn)行去重的實(shí)現(xiàn)

    本文主要介紹了Golang使用泛型對(duì)數(shù)組進(jìn)行去重的實(shí)現(xiàn),通過使用類型參數(shù)T和類型約束any,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-02-02
  • GoFrame?gmap遍歷hashmap?listmap?treemap使用技巧

    GoFrame?gmap遍歷hashmap?listmap?treemap使用技巧

    這篇文章主要為大家介紹了GoFrame?gmap遍歷hashmap?listmap?treemap使用技巧的示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-06-06
  • Go語言并發(fā)控制之sync.WaitGroup使用詳解

    Go語言并發(fā)控制之sync.WaitGroup使用詳解

    這篇文章主要為大家詳細(xì)介紹了Go語言并發(fā)控制中sync.Map的原理與使用,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-02-02
  • Golang接口使用教程詳解

    Golang接口使用教程詳解

    在?Go?語言中接口包含兩種含義:它既是方法的集合,?同時(shí)還是一種類型并且在Go?語言中是隱式實(shí)現(xiàn)的。本文通過示例詳細(xì)介紹了Golang接口的使用,需要的可以參考一下
    2022-09-09
  • 分布式架構(gòu)在Go語言網(wǎng)站的應(yīng)用

    分布式架構(gòu)在Go語言網(wǎng)站的應(yīng)用

    分布式架構(gòu)是目前應(yīng)對(duì)高流量、高并發(fā)的重要解決方案,分布式架構(gòu)的核心思想是分而治之,將單臺(tái)服務(wù)器的資源劃分為多臺(tái)服務(wù)器進(jìn)行協(xié)同完成,分布式架構(gòu)應(yīng)用于Go語言網(wǎng)站中既能提升服務(wù)速度,又能降低了服務(wù)器宕機(jī)的風(fēng)險(xiǎn)
    2024-01-01
  • Go語言上下文context底層原理

    Go語言上下文context底層原理

    這篇文章主要介紹了Go語言上下文context底層原理,context是Go中用來進(jìn)程通信的一種方式,其底層是借助channl與snyc.Mutex實(shí)現(xiàn)的,更多相關(guān)內(nèi)容需要的小伙伴可以參加一下
    2022-06-06
  • Go語言defer語句的三種機(jī)制整理

    Go語言defer語句的三種機(jī)制整理

    在本篇文章里小編給大家分享的是一篇關(guān)于Go語言defer語句的三種機(jī)制整理,需要的朋友們學(xué)習(xí)下吧。
    2020-03-03
  • 通過client-go來操作K8S集群的操作方法

    通過client-go來操作K8S集群的操作方法

    本文詳細(xì)介紹了client-go的安裝、配置和使用方法,并通過示例代碼展示了如何進(jìn)行常見的Kubernetes操作,希望這些內(nèi)容能幫助大家更好地理解和使用client-go,從而提高你的Kubernetes開發(fā)效率,感興趣的朋友一起看看吧
    2024-11-11

最新評(píng)論

勃利县| 卢湾区| 西青区| 柳江县| 长海县| 上蔡县| 黄龙县| 乌审旗| 抚远县| 洮南市| 永嘉县| 剑河县| 玉屏| 奇台县| 夏河县| 台中市| 左贡县| 陇川县| 通山县| 铁岭县| 永德县| 辰溪县| 本溪市| 紫金县| 天台县| 沙河市| 永善县| 西和县| 山东省| 宁蒗| 鹿泉市| 铜鼓县| 两当县| 宿松县| 蒙山县| 马公市| 伊宁市| 大兴区| 清涧县| 武冈市| 浦北县|