Golang 在語言層面對并發編程提供支持,一種類似協程,稱作 goroutine 的機制。
只需在函數調用語句前添加 go 關鍵字,就可創建并發執行單元。開發人員無需了解任何執行細節,調度器會自動將其安排到合適的系統線程上執行。goroutine 是一種非常輕量級的實現,可在單個進程里執行成千上萬的并發任務。
事實上,入口函數 main 就以 goroutine 運行。另有與之配套的 channel 類型,用以實現 "以通訊來共享內存" 的 CSP 模式。
~~~
package main
import "time"
func main() {
go func() {
println("Hello, World!")
}()
time.Sleep(1 * time.Second)
}
~~~
輸出結果:
~~~
Hello, World!
~~~
調度器不能保證多個 goroutine 執行次序,且進程退出時不會等待它們結束。
默認情況下,進程啟動后僅允許一個系統線程服務于 goroutine。可使用環境變量或標準庫函數 runtime.GOMAXPROCS 修改,讓調度器用多個線程實現多核并行,而不僅僅是并發。
~~~
package main
import (
"math"
"sync"
)
func sum(id int) {
var x int64
for i := 0; i < math.MaxUint32; i++ {
x += int64(i)
}
println(id, x)
}
func main() {
wg := new(sync.WaitGroup)
wg.Add(2)
for i := 0; i < 2; i++ {
go func(id int) {
defer wg.Done()
sum(id)
}(i)
}
wg.Wait()
}
~~~
命令行輸入:
~~~
go build main.go
time -p ./main
~~~
輸出結果:
~~~
0 9223372030412324865
1 9223372030412324865
real 1.92 // 程序開始到結束時間差 ( CPU 時間)
user 3.80 // 用戶態所使用 CPU 時間片 (多核累加)
sys 0.01 // 內核態所使用 CPU 時間片
~~~
命令行輸入:
~~~
GOMAXPROCS=8 time -p ./main
~~~
輸出結果:
~~~
1 9223372030412324865
0 9223372030412324865
real 1.89
user 3.76 // 雖然總時間差不多,但由 2 個核并行,real 時間自然少了許多。
sys 0.00
~~~
設置golang運行的cpu核數
單核執行如果for前面或者中間不延遲,主線程不會讓出CPU,導致異步的線程無法執行,從而無法設置flag的值,從而出現死循環。
~~~
package main
import (
"fmt"
"runtime"
)
var (
flag = false
str string
)
func foo() {
flag = true
str = "setup complete!"
}
func main() {
runtime.GOMAXPROCS(1)
go foo()
for {
if flag {
break
}
}
fmt.Println(str)
}
~~~
運行的cpu核數設置成2核
runtime.GOMAXPROCS(2)
~~~
package main
import (
"fmt"
"runtime"
)
var (
flag = false
str string
)
func foo() {
flag = true
str = "setup complete!"
}
func main() {
runtime.GOMAXPROCS(2)
go foo()
for {
if flag {
break
}
}
fmt.Println(str)
}
~~~
輸出結果:
~~~
setup complete!
~~~
調用 runtime.Goexit 將立即終止當前 goroutine 執行,調度器確保所有已注冊 defer 延遲調用被執行。
~~~
package main
import (
"runtime"
"sync"
)
func main() {
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
defer wg.Done()
defer println("A.defer")
func() {
defer println("B.defer")
runtime.Goexit() // 終止當前 goroutine
println("B") // 不會執行
}()
println("A") // 不會執行
}()
wg.Wait()
}
~~~
輸出結果:
~~~
B.defer
A.defer
~~~
和協程 yield 作用類似,Gosched 讓出底層線程,將當前 goroutine 暫停,放回隊列等待下次被調度執行。
~~~
package main
import (
"runtime"
"sync"
)
func main() {
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
for i := 0; i < 6; i++ {
println(i)
runtime.Gosched()
}
defer wg.Done()
}()
for i := 0; i < 6; i++ {
wg.Add(1)
go func() {
defer wg.Done()
println("Hello, World!")
}()
}
wg.Wait()
}
~~~
輸出結果:
~~~
Hello, World!
Hello, World!
0
1
Hello, World!
Hello, World!
Hello, World!
2
3
4
5
Hello, World!
每次輸出結果都不一樣
~~~
runtime.Gosched()用于讓出CPU時間片。這就像跑接力賽,A跑了一會碰到代碼runtime.Gosched()就把接力棒交給B了,A歇著了,B繼續跑。
goroutine中使用recover
應用場景,如果某個goroutine panic了,而且這個goroutine里面沒有捕獲(recover),那么整個進程就會掛掉。所以,好的習慣是每當go產生一個goroutine,就需要寫下recover。
~~~
package main
import (
"fmt"
// "runtime"
"time"
)
func test() {
defer func() {
if err := recover(); err != nil {
fmt.Println("panic:", err)
}
}()
var m map[string]int
m["stu"] = 100
}
func calc() {
for {
fmt.Println("i'm calc")
time.Sleep(time.Second)
}
}
func main() {
go test()
for i := 0; i < 2; i++ {
go calc()
}
time.Sleep(time.Second * 10)
}
~~~
輸出結果:
~~~
i'm calc
i'm calc
panic: assignment to entry in nil map
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
i'm calc
~~~
- 序言
- 目錄
- 環境搭建
- Linux搭建golang環境
- Windows搭建golang環境
- Mac搭建golang環境
- Go 環境變量
- 編輯器
- vs code
- Mac 安裝vs code
- Windows 安裝vs code
- vim編輯器
- 介紹
- 1.Go語言的主要特征
- 2.golang內置類型和函數
- 3.init函數和main函數
- 4.包
- 1.工作空間
- 2.源文件
- 3.包結構
- 4.文檔
- 5.編寫 Hello World
- 6.Go語言 “ _ ”(下劃線)
- 7.運算符
- 8.命令
- 類型
- 1.變量
- 2.常量
- 3.基本類型
- 1.基本類型介紹
- 2.字符串String
- 3.數組Array
- 4.類型轉換
- 4.引用類型
- 1.引用類型介紹
- 2.切片Slice
- 3.容器Map
- 4.管道Channel
- 5.指針
- 6.自定義類型Struct
- 流程控制
- 1.條件語句(if)
- 2.條件語句 (switch)
- 3.條件語句 (select)
- 4.循環語句 (for)
- 5.循環語句 (range)
- 6.循環控制Goto、Break、Continue
- 函數
- 1.函數定義
- 2.參數
- 3.返回值
- 4.匿名函數
- 5.閉包、遞歸
- 6.延遲調用 (defer)
- 7.異常處理
- 8.單元測試
- 壓力測試
- 方法
- 1.方法定義
- 2.匿名字段
- 3.方法集
- 4.表達式
- 5.自定義error
- 接口
- 1.接口定義
- 2.執行機制
- 3.接口轉換
- 4.接口技巧
- 面向對象特性
- 并發
- 1.并發介紹
- 2.Goroutine
- 3.Chan
- 4.WaitGroup
- 5.Context
- 應用
- 反射reflection
- 1.獲取基本類型
- 2.獲取結構體
- 3.Elem反射操作基本類型
- 4.反射調用結構體方法
- 5.Elem反射操作結構體
- 6.Elem反射獲取tag
- 7.應用
- json協議
- 1.結構體轉json
- 2.map轉json
- 3.int轉json
- 4.slice轉json
- 5.json反序列化為結構體
- 6.json反序列化為map
- 終端讀取
- 1.鍵盤(控制臺)輸入fmt
- 2.命令行參數os.Args
- 3.命令行參數flag
- 文件操作
- 1.文件創建
- 2.文件寫入
- 3.文件讀取
- 4.文件刪除
- 5.壓縮文件讀寫
- 6.判斷文件或文件夾是否存在
- 7.從一個文件拷貝到另一個文件
- 8.寫入內容到Excel
- 9.日志(log)文件
- server服務
- 1.服務端
- 2.客戶端
- 3.tcp獲取網頁數據
- 4.http初識-瀏覽器訪問服務器
- 5.客戶端訪問服務器
- 6.訪問延遲處理
- 7.form表單提交
- web模板
- 1.渲染終端
- 2.渲染瀏覽器
- 3.渲染存儲文件
- 4.自定義io.Writer渲染
- 5.模板語法
- 時間處理
- 1.格式化
- 2.運行時間
- 3.定時器
- 鎖機制
- 互斥鎖
- 讀寫鎖
- 性能比較
- sync.Map
- 原子操作
- 1.原子增(減)值
- 2.比較并交換
- 3.導入、導出、交換
- 加密解密
- 1.md5
- 2.base64
- 3.sha
- 4.hmac
- 常用算法
- 1.冒泡排序
- 2.選擇排序
- 3.快速排序
- 4.插入排序
- 5.睡眠排序
- 限流器
- 日志包
- 日志框架logrus
- 隨機數驗證碼
- 生成指定位數的隨機數
- 生成圖形驗證碼
- 編碼格式轉換
- UTF-8與GBK
- 解決中文亂碼
- 設計模式
- 創建型模式
- 單例模式
- singleton.go
- singleton_test.go
- 抽象工廠模式
- abstractfactory.go
- abstractfactory_test.go
- 工廠方法模式
- factorymethod.go
- factorymethod_test.go
- 原型模式
- prototype.go
- prototype_test.go
- 生成器模式
- builder.go
- builder_test.go
- 結構型模式
- 適配器模式
- adapter.go
- adapter_test.go
- 橋接模式
- bridge.go
- bridge_test.go
- 合成/組合模式
- composite.go
- composite_test.go
- 裝飾模式
- decoretor.go
- decorator_test.go
- 外觀模式
- facade.go
- facade_test.go
- 享元模式
- flyweight.go
- flyweight_test.go
- 代理模式
- proxy.go
- proxy_test.go
- 行為型模式
- 職責鏈模式
- chainofresponsibility.go
- chainofresponsibility_test.go
- 命令模式
- command.go
- command_test.go
- 解釋器模式
- interpreter.go
- interperter_test.go
- 迭代器模式
- iterator.go
- iterator_test.go
- 中介者模式
- mediator.go
- mediator_test.go
- 備忘錄模式
- memento.go
- memento_test.go
- 觀察者模式
- observer.go
- observer_test.go
- 狀態模式
- state.go
- state_test.go
- 策略模式
- strategy.go
- strategy_test.go
- 模板模式
- templatemethod.go
- templatemethod_test.go
- 訪問者模式
- visitor.go
- visitor_test.go
- 數據庫操作
- golang操作MySQL
- 1.mysql使用
- 2.insert操作
- 3.select 操作
- 4.update 操作
- 5.delete 操作
- 6.MySQL事務
- golang操作Redis
- 1.redis介紹
- 2.golang鏈接redis
- 3.String類型 Set、Get操作
- 4.String 批量操作
- 5.設置過期時間
- 6.list隊列操作
- 7.Hash表
- 8.Redis連接池
- 其它Redis包
- go-redis/redis包
- 安裝介紹
- String 操作
- List操作
- Set操作
- Hash操作
- golang操作ETCD
- 1.etcd介紹
- 2.鏈接etcd
- 3.etcd存取
- 4.etcd監聽Watch
- golang操作kafka
- 1.kafka介紹
- 2.寫入kafka
- 3.kafka消費
- golang操作ElasticSearch
- 1.ElasticSearch介紹
- 2.kibana介紹
- 3.寫入ElasticSearch
- NSQ
- 安裝
- 生產者
- 消費者
- zookeeper
- 基本操作測試
- 簡單的分布式server
- Zookeeper命令行使用
- GORM
- gorm介紹
- gorm查詢
- gorm更新
- gorm刪除
- gorm錯誤處理
- gorm事務
- sql構建
- gorm 用法介紹
- Go操作memcached
- beego框架
- 1.beego框架環境搭建
- 2.參數配置
- 1.默認參數
- 2.自定義配置
- 3.config包使用
- 3.路由設置
- 1.自動匹配
- 2.固定路由
- 3.正則路由
- 4.注解路由
- 5.namespace
- 4.多種數據格式輸出
- 1.直接輸出字符串
- 2.模板數據輸出
- 3.json格式數據輸出
- 4.xml格式數據輸出
- 5.jsonp調用
- 5.模板處理
- 1.模板語法
- 2.基本函數
- 3.模板函數
- 6.請求處理
- 1.GET請求
- 2.POST請求
- 3.文件上傳
- 7.表單驗證
- 1.表單驗證
- 2.定制錯誤信息
- 3.struct tag 驗證
- 4.XSRF過濾
- 8.靜態文件處理
- 1.layout設計
- 9.日志處理
- 1.日志處理
- 2.logs 模塊
- 10.會話控制
- 1.會話控制
- 2.session 包使用
- 11.ORM 使用
- 1.鏈接數據庫
- 2. CRUD 操作
- 3.原生 SQL 操作
- 4.構造查詢
- 5.事務處理
- 6.自動建表
- 12.beego 驗證碼
- 1.驗證碼插件
- 2.驗證碼使用
- beego admin
- 1.admin安裝
- 2.admin開發
- beego 熱升級
- beego實現https
- gin框架
- 安裝使用
- 路由設置
- 模板處理
- 文件上傳
- gin框架中文文檔
- gin錯誤總結
- 項目
- 秒殺項目
- 日志收集
- 面試題
- 面試題一
- 面試題二
- 錯題集
- Go語言陷阱和常見錯誤
- 常見語法錯誤
- 初級
- 中級
- 高級
- Go高級應用
- goim
- goim 啟動流程
- goim 工作流程
- goim 結構體
- gopush
- gopush工作流程
- gopush啟動流程
- gopush業務流程
- gopush應用
- gopush新添功能
- gopush壓力測試
- 壓測注意事項
- rpc
- HTTP RPC
- TCP RPC
- JSON RPC
- 常見RPC開源框架
- pprof
- pprof介紹
- pprof應用
- 使用pprof及Go 程序的性能優化
- 封裝 websocket
- cgo
- Golang GC
- 查看程序運行過程中的GC信息
- 定位gc問題所在
- Go語言 demo
- 用Go語言計算一個人的年齡,生肖,星座
- 超簡易Go語言實現的留言板代碼
- 信號處理模塊,可用于在線加載配置,配置動態加載的信號為SIGHUP
- 陽歷和陰歷相互轉化的工具類 golang版本
- 錯誤總結
- 網絡編程
- 網絡編程http
- 網絡編程tcp
- Http請求
- Go語言必知的90個知識點
- 第三方庫應用
- cli應用
- Cobra
- 圖表庫
- go-echarts
- 開源IM
- im_service
- 機器學習庫
- Tensorflow
- 生成二維碼
- skip2/go-qrcode生成二維碼
- boombuler/barcode生成二維碼
- tuotoo/qrcode識別二維碼
- 日志庫
- 定時任務
- robfig/cron
- jasonlvhit/gocron
- 拼多多開放平臺 SDK
- Go編譯
- 跨平臺交叉編譯
- 一問一答
- 一問一答(一)
- 為什么 Go 標準庫中有些函數只有簽名,沒有函數體?
- Go開發的應用
- etcd
- k8s
- Caddy
- nsq
- Docker
- web框架