您好,登錄后才能下訂單哦!
sync提供基本的同步原語,如sync.Mutex,sync.RWMutex,sync.Once,sync.Cond,sync.Waitgroup,除了Once和WaitGroup類型外,大多數(shù)類型都供低級(jí)庫使用。Go語言中,不要通過共享內(nèi)存通信,而要通過通信共享內(nèi)存,通過Channel和溝通可以更好地完成更高級(jí)別的同步。
type Locker interface {
Lock()
Unlock()
}
Locker提供了鎖的兩個(gè)操作方法,Lock、Unlock。
sync.Mutex是互斥鎖,是Locker的一種實(shí)現(xiàn)。
一個(gè)互斥鎖只能同時(shí)被一個(gè)goroutine鎖定,其它goroutine將阻塞直到互斥鎖被解鎖(重新爭(zhēng)搶對(duì)互斥鎖的鎖定)。
sync.Mutex使用注意:
(1)在首次使用后不要復(fù)制互斥鎖。
(2)對(duì)一個(gè)未鎖定的互斥鎖解鎖將會(huì)產(chǎn)生運(yùn)行時(shí)錯(cuò)誤。
使用示例:
package main
import (
"fmt"
"sync"
"time"
)
var locker sync.Mutex
func mutexDemo() {
var value int = 0
for i := 0; i < 100; i++ {
go func(i int) {
locker.Lock()
defer locker.Unlock()
fmt.Printf("Goroutine %d : value: %d\n", i, value)
value++
}(i)
}
}
func main() {
mutexDemo()
time.Sleep(time.Second)
}
sync.Pool?臨時(shí)對(duì)象池用于存儲(chǔ)臨時(shí)對(duì)象,將使用完畢的對(duì)象存入對(duì)象池中,在需要的時(shí)候取出來重復(fù)使用,目的是為了避免重復(fù)創(chuàng)建相同的對(duì)象造成GC負(fù)擔(dān)過重。如果對(duì)象不再被其它變量引用,存放的臨時(shí)對(duì)象可能會(huì)被GC回收掉。
type Pool struct {
// 創(chuàng)建臨時(shí)對(duì)象的函數(shù)
New func() interface{}
}
// 向臨時(shí)對(duì)象池中存入對(duì)象
func (p *Pool) Put(x interface{})
// 從臨時(shí)對(duì)象池中取出對(duì)象
func (p *Pool) Get() interface{}
從sync.Pool中取出對(duì)象時(shí),如果Pool中沒有對(duì)象,將返回nil,但如果給 Pool.New字段指定一個(gè)函數(shù),Pool將使用指定函數(shù)創(chuàng)建一個(gè)新對(duì)象返回。
sync.Pool可以安全的在多個(gè)goroutine中并行使用,但并不適用于所有空閑對(duì)象,應(yīng)該用來管理并發(fā)的例程共享的臨時(shí)對(duì)象,而不應(yīng)該管理短壽命對(duì)象中的臨時(shí)對(duì)象。
sync.Pool使用示例如下:
package main
import (
"bytes"
"io"
"os"
"sync"
"time"
)
var bufPool = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
func Log(w io.Writer, key, val string) {
// 獲取臨時(shí)對(duì)象,沒有則自動(dòng)創(chuàng)建
b := bufPool.Get().(*bytes.Buffer)
b.Reset()
b.WriteString(time.Now().Format(time.RFC3339))
b.WriteByte(' ')
b.WriteString(key)
b.WriteByte('=')
b.WriteString(val)
w.Write(b.Bytes())
// 將臨時(shí)對(duì)象放回到Pool中
bufPool.Put(b)
}
func main() {
Log(os.Stdout, "key", "value")
}
// output:
// 2018-12-31T15:57:27+08:00 key=value
sync.Once可以使得函數(shù)多次調(diào)用只執(zhí)行一次。
type Once struct {
m Mutex
done uint32
}
func (o *Once) Do(f func())
用done來記錄執(zhí)行次數(shù),用互斥鎖m來保證僅被執(zhí)行一次。只有一個(gè)Do方法,調(diào)用執(zhí)行。
利用sync.Once實(shí)現(xiàn)單例模式代碼如下:
package Singleton
import (
"sync"
)
type Singleton map[string]string
var (
instance Singleton
once sync.Once
)
func New() Singleton{
once.Do(func() {
instance = make(Singleton)
})
return instance
}
使用示例如下:
package main
import (
"fmt"
"DesignPattern/Singleton"
)
func main() {
instance1 := Singleton.New()
instance1["name"] = "Jack Bauer"
instance2 := Singleton.New()
fmt.Println("My name is", instance2["name"])
}
// output:
// My name is Jack Bauer
sync.RWMutex是針對(duì)讀寫操作的互斥鎖,讀寫鎖與互斥鎖最大的不同就是可以分別對(duì)讀、寫進(jìn)行鎖定。一般用在大量讀操作、少量寫操作的情況。sync.RWMutex提供四種操作方法:
func (rw *RWMutex) Lock()
func (rw *RWMutex) Unlock()
func (rw *RWMutex) RLock()
func (rw *RWMutex) RUnlock()
RLock對(duì)讀操作進(jìn)行鎖定,RUnlock對(duì)讀鎖定進(jìn)行解鎖,Lock對(duì)寫操作進(jìn)行鎖定,Unlock對(duì)寫鎖定進(jìn)行解鎖。
sync.RWMutex鎖定規(guī)則如下:
(1)同時(shí)只能有一個(gè)goroutine能夠獲得寫鎖定。
(2)同時(shí)可以有任意多個(gè)gorouinte獲得讀鎖定。
(3)同時(shí)只能存在寫鎖定或讀鎖定(讀和寫互斥)。
(4)當(dāng)有一個(gè)goroutine獲得寫鎖定,其它無論是讀鎖定還是寫鎖定都將阻塞直到寫解鎖;當(dāng)有一個(gè)goroutine獲得讀鎖定,其它讀鎖定任然可以繼續(xù);當(dāng)有一個(gè)或任意多個(gè)讀鎖定,寫鎖定將等待所有讀鎖定解鎖后才能夠進(jìn)行寫鎖定。
sync.RWMutex讀寫鎖使用注意:
(1)在首次使用之后,不要復(fù)制讀寫鎖。
(2)不要混用鎖定和解鎖,如:Lock和RUnlock、RLock和Unlock。對(duì)未讀鎖定的讀寫鎖進(jìn)行讀解鎖或?qū)ξ磳戞i定的讀寫鎖進(jìn)行寫解鎖將會(huì)引起運(yùn)行時(shí)錯(cuò)誤。
使用示例代碼:
package main
import (
"fmt"
"sync"
"time"
)
var rw sync.RWMutex
func RWMutexDemo() {
var value int = 0
for i := 0; i < 10; i++ {
go func(i int) {
rw.Lock()
defer rw.Unlock()
fmt.Printf("Goroutine %d : Write value: %d\n", i, value)
value++
}(i)
go func(i int) {
rw.RLock()
defer rw.RUnlock()
fmt.Printf("Goroutine %d : Read value: %d\n", i, value)
}(i)
}
}
func main() {
RWMutexDemo()
time.Sleep(time.Minute)
}
sync.WaitGroup用于等待一組goroutine結(jié)束。
sync.WaitGroup操作方法如下:
func (wg *WaitGroup) Add(delta int)
func (wg *WaitGroup) Done()
func (wg *WaitGroup) Wait()
Add用來添加goroutine的個(gè)數(shù)。Done執(zhí)行一次數(shù)量減1。Wait用來等待結(jié)束。
sync.WaitGroup使用示例如下:
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func WaitGroupDemo() {
var value int = 0
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Printf("Goroutine %d : Write value: %d\n", i, value)
value++
}(i)
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Printf("Goroutine %d : Read value: %d\n", i, value)
}(i)
}
}
func main() {
WaitGroupDemo()
wg.Wait()
}
sync.Cond實(shí)現(xiàn)一個(gè)條件等待變量,即等待或宣布事件發(fā)生的goroutine的會(huì)合點(diǎn)。
func NewCond(l Locker) *Cond
func (c *Cond) Broadcast()
func (c *Cond) Signal()
func (c *Cond) Wait()
NewCond用于根據(jù)Locker創(chuàng)建一個(gè)條件等待變量,Wait用于讓一個(gè)goroutine等待通知,Signal用于單次發(fā)送通知讓等待的goroutine繼續(xù),Broadcast用于廣播通知讓所有等待的goroutine繼續(xù)。
sync.Cond條件等待變量實(shí)現(xiàn)生產(chǎn)者-消費(fèi)者模式示例如下:
package main
import (
"fmt"
"math/rand"
"sync"
"time"
)
var locker = new(sync.Mutex)
var cond = sync.NewCond(locker)
var capacity = 10
var consumerNum = 5
var producerNum = 2
func Produce(out chan<- int) {
for i := 0; i < producerNum; i++ {
go func(nu int) {
for {
cond.L.Lock()
for len(out) == capacity {
fmt.Println("Capacity Full, stop Produce")
cond.Wait()
}
num := rand.Intn(100)
out <- num
fmt.Printf("Producer %d produce: num %d\n", nu, num)
cond.L.Unlock()
cond.Signal()
time.Sleep(time.Microsecond * 500)
}
}(i)
}
}
func Consume(in <-chan int) {
for i := 0; i < consumerNum; i++ {
go func(nu int) {
for {
cond.L.Lock()
for len(in) == 0 {
fmt.Println("Capacity Empty, stop Consume")
cond.Wait()
}
num := <-in
fmt.Printf("Consumer %d: consume num %d\n", nu, num)
cond.L.Unlock()
time.Sleep(time.Second)
cond.Signal()
}
}(i)
}
}
func main() {
rand.Seed(time.Now().UnixNano())
quit := make(chan bool)
ProductPool := make(chan int, capacity)
Produce(ProductPool)
Consume(ProductPool)
<-quit
}
在計(jì)算機(jī)科學(xué)領(lǐng)域,反射是指能夠自描述和自控制的應(yīng)用。反射通過采用某種機(jī)制來實(shí)現(xiàn)對(duì)自己行為的描述(self-representation)和監(jiān)測(cè)(examination),并能根據(jù)自身行為的狀態(tài)和結(jié)果,調(diào)整或修改應(yīng)用所描述行為的狀態(tài)和相關(guān)的語義。
每種語言的反射模型都不同,并且某些語言不支持反射。Golang語言通過reflect包實(shí)現(xiàn)反射機(jī)制,在運(yùn)行時(shí)動(dòng)態(tài)的調(diào)用對(duì)象的方法和屬性。
Go語言中的變量包括(type, value)兩部分,type包括static type和concrete type。static typ是編碼時(shí)的類型(如int、string),concrete type是runtime的類型。
類型斷言能否成功,取決于變量的concrete type,而不是static type。因此,一個(gè)reader變量如果其concrete type實(shí)現(xiàn)了write方法,也可以被類型斷言為writer。
Golang指定類型變量的類型是靜態(tài)類型,在創(chuàng)建變量時(shí)類型就已經(jīng)確定,因此,反射主要與Golang的interface類型相關(guān)(type是concrete type)。
在Golang的實(shí)現(xiàn)中,每個(gè)interface變量都有一個(gè)(value, type)對(duì)用于記錄變量的實(shí)際值和類型。value是變量的實(shí)際值,type是變量的實(shí)際類型。interface類型的變量包含2個(gè)指針,一個(gè)指針指向值的類型(concrete type),另一個(gè)指針指向?qū)嶋H的值(value)。
func TypeOf(i interface{}) Type
TypeOf用來動(dòng)態(tài)獲取輸入?yún)?shù)接口中的值的類型,如果接口為空則返回nil。func ValueOf(i interface{}) Value
ValueOf用來獲取輸入?yún)?shù)接口中的數(shù)據(jù)的值,如果接口為空則返回0。
reflect.ValueOf(interface)得到一個(gè)relfect.Value變量,可以通過relfect.Value本身的Interface()方法獲得接口變量的真實(shí)內(nèi)容,然后可以通過類型判斷進(jìn)行轉(zhuǎn)換,轉(zhuǎn)換為原有真實(shí)類型。真實(shí)類型可能是已知原有類型,也可能是未知原有類型。
對(duì)于已知原有類型:
package main
import (
"fmt"
"reflect"
)
func main() {
var num float64 = 3.14
value := reflect.ValueOf(num)
pointer := reflect.ValueOf(&num)
fmt.Println("value: ", value.Interface().(float64))
fmt.Println("value: ", pointer.Interface().(*float64))
fmt.Println("type: ", reflect.TypeOf(num))
}
// output:
// value: 3.14
// value: 0xc42001e0e8
// type: float64
對(duì)于未知原有類型,需要進(jìn)行遍歷探測(cè)其Filed:
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string
ID int
Age int
}
func (student Student) Print() {
fmt.Printf("%s's ID is %d, %d years.", student.Name, student.ID, student.Age)
}
func handleFieldAndMethod(input interface{}) {
inputType := reflect.TypeOf(input)
fmt.Println("type: ", inputType.Name())
inputValue := reflect.ValueOf(input)
fmt.Println("value: ", inputValue)
// 獲取方法字段
// 1. 先獲取interface的reflect.Type,然后通過NumField進(jìn)行遍歷
// 2. 再通過reflect.Type的Field獲取其Field
// 3. 最后通過Field的Interface()得到對(duì)應(yīng)的value
for i := 0; i < inputType.NumField(); i++ {
field := inputType.Field(i)
value := inputValue.Field(i).Interface()
fmt.Printf("%s %v %v\n", field.Name, field.Type, value)
}
// 獲取方法
// 1. 先獲取interface的reflect.Type,然后通過.NumMethod進(jìn)行遍歷
for i := 0; i < inputType.NumMethod(); i++ {
m := inputType.Method(i)
fmt.Printf("%s: %v\n", m.Name, m.Type)
}
}
func main() {
bauer := Student{"Bauer", 1, 130}
handleFieldAndMethod(bauer)
}
// output:
// type: Student
// value: {Bauer 1 130}
// Name string Bauer
// ID int 1
// Age int 130
// Print: func(main.Student)
func (v Value) MethodByName(name string) Value
MethodByName返回一個(gè)函數(shù)值對(duì)應(yīng)的reflect.Value方法的名字。func (v Value) Call(in []Value) []Value
Call方法將最終調(diào)用真實(shí)的方法,參數(shù)必須一致。
package main
import (
"fmt"
"reflect"
)
type Student struct {
Name string
ID int
Age int
}
func (student Student) Print() {
fmt.Printf("%s's ID is %d, %d years.", student.Name, student.ID, student.Age)
}
func CallMethod() {
student := Student{"Bauer", 1, 20}
value := reflect.ValueOf(student)
methodValue := value.MethodByName("Print")
// 對(duì)于無參函數(shù)
args := make([]reflect.Value, 0)
methodValue.Call(args)
}
func main() {
CallMethod()
}
// output:
// Print: func(main.Student)
Go語言編譯器產(chǎn)生本地可執(zhí)行代碼,可執(zhí)行代碼仍舊運(yùn)行在Go的 runtime中,runtime負(fù)責(zé)管理包括內(nèi)存分配、垃圾回收、棧處理、goroutine、channel、切片(slice)、map 和反射(reflection)等。
runtime與C標(biāo)準(zhǔn)庫的作用一樣都是為了語言的跨平臺(tái)性,runtime可以運(yùn)行在Windows和Unix平臺(tái),可以運(yùn)行在Intel或ARM處理器上。Go程序都附帶runtime,runtime負(fù)責(zé)與底層操作系統(tǒng)交互。
runtime.Gosched()
讓當(dāng)前goroutine讓出?cpu?以讓其它goroutine運(yùn)行,不會(huì)掛起當(dāng)前線程,因此當(dāng)前線程未來會(huì)繼續(xù)執(zhí)行。func NumCPU() int
獲取當(dāng)前系統(tǒng)的邏輯CPU?核數(shù)量runtime.GOMAXPROCS(i int)
設(shè)置最大的可同時(shí)使用的?CPU
通過runtime.GOMAXPROCS函數(shù),應(yīng)用程序何以在運(yùn)行期間設(shè)置運(yùn)行時(shí)系統(tǒng)中得邏輯處理器P最大數(shù)量。應(yīng)在應(yīng)用程序最早的調(diào)用。并且最好的設(shè)置P最大值的方法是在運(yùn)行Go程序之前設(shè)置好操作程序的環(huán)境變量GOMAXPROCS,而不是在程序中調(diào)用runtime.GOMAXPROCS函數(shù)。
無論傳遞給函數(shù)的整數(shù)值是什么值,運(yùn)行時(shí)系統(tǒng)的P最大值總會(huì)在1~256之間。runtime.Goexit()
退出當(dāng)前?goroutine(但defer語句會(huì)照常執(zhí)行)
runtime.Goexit函數(shù)被調(diào)用后,會(huì)立即使調(diào)用他的Groution的運(yùn)行被終止,但其他Goroutine并不會(huì)受到影響。runtime.Goexit函數(shù)在終止調(diào)用它的Goroutine的運(yùn)行之前會(huì)先執(zhí)行該Groution中還沒有執(zhí)行的defer語句。runtime.NumGoroutine()
獲取正在執(zhí)行和排隊(duì)的任務(wù)總數(shù)
runtime.NumGoroutine函數(shù)在被調(diào)用后,會(huì)返回系統(tǒng)中的處于特定狀態(tài)的Goroutine的數(shù)量。這里的特指是指Grunnable\Gruning\Gsyscall\Gwaition。處于這些狀態(tài)的Groutine即被看做是活躍的或者說正在被調(diào)度。
注意:垃圾回收所在Groutine的狀態(tài)也處于這個(gè)范圍內(nèi)的話,也會(huì)被納入該計(jì)數(shù)器。runtime.GOOS
獲取目標(biāo)操作系統(tǒng)func GOROOT() string
獲取當(dāng)前GOROOT
CGO是實(shí)現(xiàn)Go與C互操作的方式,包括Go調(diào)C和C調(diào)Go兩個(gè)過程。Go調(diào)用C需要在程序中引入的一個(gè)偽包,import “C”即為在Go中使用的偽包。C偽包會(huì)在編譯前被CGO工具捕捉到,并做一些代碼的改寫和樁文件的生成,不會(huì)被Go編譯器見到。
Go語言中調(diào)用C程序的方法如下:
//自定義函數(shù)調(diào)用
package main
/*
#include <stdio.h>
#include <stdlib.h>
void output(char *str) {
printf("%s\n", str);
}
*/
import "C"
import "unsafe"
func main() {
str := C.CString("hello cgo")
C.output(str)
C.free(unsafe.Pointer(str))
}
每一個(gè)Go程序都附帶一個(gè)runtime,runtime負(fù)責(zé)與底層操作系統(tǒng)交互,也都會(huì)有scheduler對(duì)goruntines進(jìn)行調(diào)度。
Golang是靜態(tài)編譯型語言,在編譯時(shí)就將所有引用的包全部加載打包到最終的可執(zhí)行程序中,因此不能在運(yùn)行時(shí)動(dòng)態(tài)加載其它共享庫。Go 1.8開始,Go語言Linux和MacOS版本通過plugin包提供插件化加載共享庫機(jī)制,能夠在運(yùn)行時(shí)動(dòng)態(tài)加載外部功能。
type Plugin struct {
pluginpath string
err string // set if plugin failed to load
loaded chan struct{} // closed when loaded
syms map[string]interface{}
}
func Open(path string) (*Plugin, error)
func (p *Plugin) Lookup(symName string) (Symbol, error)
type Symbol interface{}
Open: 根據(jù)參數(shù)path提供的插件路徑加載插件,并返回插件結(jié)構(gòu)的指針*Plugin
。
Lookup:?*Plugin
的惟一方法,通過名稱symName在插件中尋找對(duì)應(yīng)的變量或方法,以Symbol的形式返回。從插件中找到的任何元素都是以Symbol形式(即interface{})返回,需要通過斷言的形式對(duì)結(jié)果進(jìn)行判斷和轉(zhuǎn)換,得到需要的類型。
Go語言編譯器使用-buildmode=plugin標(biāo)記編譯生成一個(gè)插件(共享對(duì)象庫文件)。Go包中導(dǎo)出的函數(shù)和變量被公開為ELF符號(hào),可以使用plugin包在運(yùn)行時(shí)查找并綁定ELF符號(hào)。go build -buildmode=plugin -o xxxplugin.so xxxplugin.go
如果要想更好的控制插件版本,實(shí)現(xiàn)熱更新插件,可以采用自動(dòng)注冊(cè)插件方式。當(dāng)新版本插件加載后,自動(dòng)注冊(cè)插件版本號(hào),插件平臺(tái)里優(yōu)先使用新版本插件的方法。
插件編寫HelloPlugin.go:
package main
import (
"fmt"
)
func init() {
fmt.Println("Hello")
}
func Hello() {
fmt.Println("world")
}
插件編譯:go build -buildmode=plugin -o HelloPlugin.so HelloPlugin.go
插件調(diào)用main.go:
package main
import (
"fmt"
"plugin"
)
func main() {
open, err := plugin.Open("./HelloPlugin.so")
if err != nil {
fmt.Println(err.Error())
}
symbol, err := open.Lookup("Hello")
if err != nil {
fmt.Println(err)
}
symbol.(func())()
}
編譯運(yùn)行:go run main.go
插件管理器實(shí)現(xiàn):
package PluginManager
import "fmt"
// 插件容器
var Plugins map[string]Plugin
func init() {
Plugins = make(map[string]Plugin)
}
type Plugin interface {
Start()
}
// 啟動(dòng)這個(gè)容器中所有的插件
func Start() {
for name, plugin := range Plugins {
go plugin.Start()
fmt.Printf("%s Plugin Start.\n", name)
}
}
// 插件做完之后必須得插入到容器中
func Register(name string, plugin Plugin) {
Plugins[name] = plugin
}
插件實(shí)現(xiàn):
package HelloPlugin
import (
"GoExample/Plugin/PluginManager"
"fmt"
)
type HelloPlugin struct {
}
// 導(dǎo)入包時(shí)注冊(cè)插件
func init() {
plugin := HelloPlugin{}
PluginManager.Register("HelloPlugin", plugin)
}
func (this HelloPlugin) Start() {
fmt.Println("This is HelloPlugin.")
}
插件使用:
package main
import "GoExample/Plugin/PluginManager"
import _ "GoExample/Plugin/HelloPlugin"
func main() {
PluginManager.Start()
}
// output:
// HelloPlugin Plugin Start.
os/signal包可以實(shí)現(xiàn)對(duì)信號(hào)的處理,Notify方法用來監(jiān)聽收到的信號(hào),Stop方法用來取消監(jiān)聽。func Notify(c chan < - os.Signal, sig ...os.Signal)
Notify函數(shù)會(huì)將進(jìn)程收到的系統(tǒng)Signal轉(zhuǎn)發(fā)給channel c,轉(zhuǎn)發(fā)哪些信號(hào)由可變參數(shù)決定,SIGKILL和SIGSTOP不能被攔截和處理。
參數(shù)c表示接收信號(hào)的channel,后續(xù)參數(shù)表示設(shè)置要監(jiān)聽的信號(hào),如果不設(shè)置表示監(jiān)聽所有的信號(hào)。func Stop(c chan < - os.Signal)
Stop方法取消監(jiān)聽通道上的所有信號(hào)。
package main
import (
"fmt"
"os"
"os/signal"
"syscall"
)
func main() {
signalChannel := make(chan os.Signal, 1)
done := make(chan bool, 1)
// 監(jiān)聽SIGINT、SIGTERM
signal.Notify(signalChannel, syscall.SIGINT, syscall.SIGTERM)
go func(ch chan os.Signal) {
// 接收信號(hào)
channel := <-ch
fmt.Printf("Received a signal: %s\n", channel)
done <- true
}(signalChannel)
// signal.Stop(signalChannel)
for {
fmt.Println("Waiting signal.")
<-done
fmt.Println("Exiting.")
}
}
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。