網站首頁 編程語言 正文
1.goroutine-看一個需求
需求:要求統計1-900000000的數字中,那些是素數?
分析:
- 傳統方法,就是使用一個循環,循環的判斷各個數是不是素數。
- 使用并發或并行的方式,將統計素數的任務分配給多個goroutine去完成,這時就會使用到goroutine。
2.進程和線程介紹
- 進程就是程序在操作系統中的一次執行過程,是系統進行資源分配和調度的基本單位
- 線程是進程的一個執行實例,是程序執行的最小單位,它是比進程更小的能獨立運行的基本單位。
- 一個進程可以創建和銷毀多個線程,同一個進程中的多個線程可以并發執行
- 一個程序至少有一個進程,一個進程至少有一個線程
3.并發和并行
- 多線程程序在單核上運行,就是并發
- 多個程程序在多核上運行,就是并行
并發:因為是在一個CPU上,比如有10個線程,每個線程執行10毫秒(進行輪詢操作),從人的角度看,好像這10個線程都在運行,但是從微觀上看,在某一個時間點看,其實只有一個線程在執行,這就是并發。
并行:因為是在多個CPU上(比如有10個CPU),比如有10個線程,每個線程執行10毫秒(各自在不同CPU上執行),從人的角度看,這10個線程都在運行,但是從微觀上看,在某一個時間點看,也同時有10個線程在執行,這就是并行
4.Go協程和Go主線程
Go主線程(有程序員直接稱為線程/也可以理解成進程):一個Go線程上,可以起多個攜程,你可以這樣理解,攜程是輕量的線程
Go協程的特點
有獨立的棧空間
共享程序堆空間
調度由用戶控制
攜程是輕量級的線程
案例說明
請編寫一個程序,完成如下功能:
1.在主線程(可以理解成進程)中,開啟一個goroutine,該攜程每隔1秒輸出“hello,world”
2.在主線程中也每隔一秒輸出“hello,golang”,輸出10次后,退出程序
3.要求主線程和goroutine同時執行
4.畫出主線程和協程執行流程圖
代碼實現
// 在主線程(可以理解成進程)中,開啟一個goroutine,該協程每秒輸出 “hello,world” // 在主線程中也每隔一秒輸出“hello,golang”,輸出10次后,退出程序 // 要求主線程和goroutine同時執行 //編寫一個函數,每隔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() // 開啟了一個協程 for i:=1;i<=10;i++{ fmt.Println(" main() hello,golang"+strconv.Itoa(i)) time.Sleep(time.Second) } }
總結
- 主線程是一個物理線程,直接作用在CPU上的,是重量級的,非常耗費CPU資源。
- 協程從主線程開啟的,是輕量級的線程,是邏輯態。對資源消耗相對少。
- Golang的協程機制是重要的特點,可以輕松的開啟上萬個協程。其他編程語言的并發機制是一般基于線程的,開啟過多的線程,資源耗費大,這里就突顯Golang在并發上的優勢了
MPG模式基本介紹
M:操作系統的主線程(是物理線程)
P:協程執行需要的上下文
G:協程
5.設置Golang運行的CPU數
介紹:為了充分利用多CPU的優勢,在Golang程序中設置運行的CPU數目
package main import "fmt" import "runtime" func main(){ // 獲取當前系統CPU的數量 num := runtime.NumCPU() // 這里設置num-1的CPU運行go程序 runtime.GOMAXPROCS(num) fmt.Println("num=",num) }
- go1.8后,默認讓程序運行在多個核上,可以不用設置了
- go1.8前,還是要設置一下,可以更高效的利用CPU
6.channel(管道)看需求
需求:現在要計算 1-200的各個數的階乘,并且把各個數的階乘放入到map中。最后顯示出來。要求使用goroutine完成
分析思路:
使用goroutine來完成,效率高,但是會出現并發/并行安全問題
這里就提出了不同goroutine如何通信的問題
代碼實現
使用goroutine來完成(看看使用gorotine并發完成會出現什么問題?然后我們會去解決)
在運行某個程序時,如何知道是否存在資源競爭問題,方法很簡單,在編譯該程序時,增加一個參數 -race即可
不同goroutine之間如何通訊
1.全局變量的互斥鎖
2.使用管道channel來解決
使用全局變量加鎖同步改進程序
- 英文沒有對全局變量m加鎖,因此會出現資源爭奪問題,代碼會出現錯誤,提示concurrent map writes
- 解決方案:加入互斥鎖
- 我們的數的階乘很大,結果會越界,可以將求階乘改成sum += uint64(i)
源碼
package main import ( "fmt" "time" "sync" ) // 需求:現在要計算 1-200的各個數的階乘,并且把各個數的階乘放入到map中 // 最后顯示出來。要求使用goroutine完成 // 思路 // 1. 編寫一個函數,來計算各個數的階乘,并放入到map中 // 2. 我們啟動的協程多個,統計的將結果放入到map中 // 3. map應該做出一個全局的 var ( myMap = make(map[int]int,10) // 聲明一個全局的互斥鎖 // lock 是一個全局的互斥鎖 //sync 是包:synchornized 同步 // Mutex: 是互斥 lock sync.Mutex ) // test函數就是計算n!,讓將這個結果放入到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(){ // 我們這里開啟多個協程完成這個任務[200個] for i := 1;i<=20;i++{ go test(i) } // 休眠10秒鐘【第二個問題】 time.Sleep(time.Second * 10) lock.Lock() // 這里我們輸出結果 變量這個結果 for i,v := range myMap{ fmt.Printf("map[%d]=%d\n",i,v) } lock.Unlock() }
channel(管道)-基本使用
channel初始化
說明:使用make進行初始化
var intChan chan int
intChan = make(chan int,10)
向channel中寫入(存放)數據
var intChan chan int
intChan = make(chan int,10)
num := 999
intChan <-10
intChan <-num
管道的初始化,寫入數據到管道,從管道讀取數據及基本的注意事項
package main import ( "fmt" ) func main(){ // 演示一下管道的使用 // 1.創建一個可以存放3個int類型的管道 var intChan chan int intChan = make(chan int,3) // 2.看看intChannel是什么 fmt.Printf("intChan 的值=%v intChan本身的地址=%p\n",intChan,&intChan) // 3.向管道寫入數據 intChan<- 10 num := 211 intChan<- num // 注意點,當我們給管寫入數據時,不能超過其容量 intChan<- 50 // intChan<- 98 //4. 看看管道的長度和cap(容量) fmt.Printf("channel len=%v cap=%v \n",len(intChan),cap(intChan)) // 2,3 // 5.從管道中讀取數據 var num2 int num2 = <-intChan fmt.Println("num2=",num2) fmt.Printf("channel len=%v cap=%v \n",len(intChan),cap(intChan)) // 2,3 // 6.在沒有使用協程的情況下,如果我們的管道數據已經全部取出,再取就會報告 deadlock num3 := <-intChan num4 := <-intChan // num5 := <-intChan fmt.Println("num3=",num3,"num4=",num4)//,"num5=",num5) }
channel使用的注意事項
1.channel中只能存放指定的數據類型
2.channel的數據放滿后,就不能再放入了
3.如果從channel取出數據后,可以繼續放入
4. 在沒有使用協程的情況下,如果channel數據取完了,再取,就會報dead lock
示例代碼
package main import ( "fmt" ) type Cat struct{ Name string Age int } func main(){ // 定義一個存放任意數據類型的管道 3個數據 // var callChan 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) }
channel的關閉
使用內置函數close可以關閉channel,當channel關閉后,就不能再向channel寫數據了,但是仍然可以從該channel讀取數據
channel的遍歷
channel支持for-range的方式進行遍歷,請注意兩個細節
- 在遍歷時,如果channel沒有關閉,則會出現deadlock的錯誤
- 在遍歷時,如果channel已經關閉,則會正常遍歷數據,遍歷完后,就會退出遍歷。
代碼演示:
package main import ( "fmt" ) func main(){ intChan := make(chan int,3) intChan<- 100 intChan<- 200 close(intChan) // close // 這是不能夠再寫入到channel // intChan<-300 fmt.Println("okook~") // 當管道關閉后,讀取數據是可以的 n1 := <-intChan fmt.Println("n1=",n1) // 遍歷管道 intChan2 := make(chan int,100) for i := 0; i< 100;i++{ intChan2<-i*2 // 放入100個數據到管道 } // 遍歷管道不能使用普通的for循環 // 在遍歷時,如果channel沒有關閉,則會出現deadlock的錯誤 // 在遍歷時,如果channel已經關閉,則會正常遍歷數據,遍歷完后,就會退出遍歷 close(intChan2) for v := range intChan2{ fmt.Println("v=",v) } }
原文鏈接:https://blog.csdn.net/ab15176142633/article/details/128353482
相關推薦
- 2022-04-26 Python?Socket?編程知識點詳細介紹_python
- 2022-10-21 一文帶你入門Go語言中定時任務庫Cron的使用_Golang
- 2022-12-12 C++?Boost?Phoenix庫示例分析使用_C 語言
- 2023-08-30 linux服務器使用rsync 和 inotify或者sersync 實現服務器之間文件實時同步
- 2022-07-04 解決Python保存文件名太長OSError:?[Errno?36]?File?name?too?l
- 2023-01-02 Android?Map數據結構全面總結分析_Android
- 2022-09-16 C++中的位運算和位圖bitmap解析_C 語言
- 2022-04-08 C語言互換最大值與最小值的位置_C 語言
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支