简介
main gotoutine 在进程开始时自动创建并启动,go func()是一个并发的函数,与其他代码一起运行.
fork-join
Go语言遵循一个成为fork-join的并发模型
fork 指的是在程序中的任意地方可以将执行的子分支与父节点同时运行
join指的是,在未来的某时刻,这些并发的分支可以合并在一起
join是保证程序正确性和消除竞争条件的关键,可以通过多种方式实现.
下面举几个有意思的小例子
func main() {
var wg sync.WaitGroup
word := "hello"
wg.Add(1)
go func() {
defer wg.Done()
word = "world"
}()
wg.Wait()
fmt.Println(word)
for _, w := range []string{"hello", ",", "world"} {
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println(w)
}()
}
wg.Wait()
for _, w := range []string{"hello", ",", "world"} {
wg.Add(1)
go func(w string) {
defer wg.Done()
fmt.Println(w)
}(w)
}
wg.Wait()
}
world
world
world
world
world
hello
,
这是因为,在go协程开始之前,循环就已经退出了,word逃逸到了堆上,因此多次引用切片中的最后一个值.
通过每次循环,将word的副本传到闭包中,保证输出的正确性.(同时可以看出,协程运行的顺序不固定)
func BenchmarkContextSwitch(b *testing.B) {
var wg sync.WaitGroup
begin := make(chan struct{})
c := make(chan struct{})
var info struct{}
sender := func() {
defer wg.Done()
<-begin
for i := 0; i < b.N; i++ {
c <- info
}
}
receiver := func() {
defer wg.Done()
<-begin
for i := 0; i < b.N; i++ {
<-c
}
}
wg.Add(2)
go sender()
go receiver()
b.StartTimer()
close(begin)
wg.Wait()
}
协程切换上下文开销很小
goos: windows
goarch: amd64
cpu: AMD Ryzen 7 5800H with Radeon Graphics
BenchmarkContextSwitch 12599324 96.11 ns/op
PASS
ok command-line-arguments 1.361s
join实现方式
锁
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var count int
increment := func() {
count++
fmt.Println("incrementing:", count)
time.Sleep(20 * time.Millisecond)
}
decrement := func() {
count--
fmt.Println("decrementing:", count)
time.Sleep(20 * time.Millisecond)
}
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
decrement()
}()
}
wg.Wait()
fmt.Println("over")
}
func main01() {
var count int
var lock sync.Mutex
increment := func() {
lock.Lock()
defer lock.Unlock()
count++
fmt.Println("incrementing:", count)
time.Sleep(20 * time.Millisecond)
}
decrement := func() {
lock.Lock()
defer lock.Unlock()
count--
fmt.Println("decrementing:", count)
time.Sleep(20 * time.Millisecond)
}
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
increment()
}()
}
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
decrement()
}()
}
wg.Wait()
fmt.Println("over")
}
pool
节约资源
func main02() {
var numCalsCreated int
calcPool := &sync.Pool{
New: func() interface{} {
numCalsCreated += 1
mem := make([]byte, 1024)
return &mem
},
}
calcPool.Put(calcPool.New())
calcPool.Put(calcPool.New())
calcPool.Put(calcPool.New())
const numWorkers = 1024 * 1024
var wg sync.WaitGroup
wg.Add(numWorkers)
for i := 0; i < numWorkers; i++ {
go func() {
defer wg.Done()
mem := calcPool.Get().(*[]byte)
defer calcPool.Put(mem)
}()
}
fmt.Println(numCalsCreated, "个实例被创建")
}
func main01() {
myPool := &sync.Pool{
New: func() interface{} {
fmt.Println("new instance")
return struct{}{}
},
}
myPool.Get()
instance := myPool.Get()
myPool.Put(instance)
myPool.Get()
myPool.Get()
}
节省时间
package main
import (
"fmt"
"io/ioutil"
"log"
"net"
"sync"
"testing"
"time"
)
func connectToService() interface{} {
time.Sleep(1 * time.Second)
return struct{}{}
}
func warmServiceConnCache() *sync.Pool {
p := &sync.Pool{
New: connectToService,
}
for i := 0; i < 10; i++ {
p.Put(p.Get())
}
return p
}
// 1312462200
// 1323923600
func startNetwork() *sync.WaitGroup {
var wg sync.WaitGroup
wg.Add(1)
go func() {
connPool := warmServiceConnCache()
server, err := net.Listen("tcp", "localhost:8080")
if err != nil {
fmt.Println("err", err)
}
defer server.Close()
wg.Done()
for {
conn, err := server.Accept()
if err != nil {
log.Println("can not accept connection")
}
svcConn := connPool.Get()
fmt.Fprintln(conn, "hello")
connPool.Put(svcConn)
conn.Close()
}
}()
return &wg
}
func init() {
workStart := startNetwork()
workStart.Wait()
}
func BenchmarkNetworkRequest(b *testing.B) {
for i := 0; i < b.N; i++ {
conn, err := net.Dial("tcp", "localhost:8080")
if err != nil {
fmt.Println("err", err)
}
if _, err := ioutil.ReadAll(conn); err != nil {
b.Fatalf("can not read")
}
conn.Close()
}
}
chanel
package main
import (
"fmt"
"time"
)
//阻塞读
func main01() {
start := time.Now()
c := make(chan interface{})
go func() {
time.Sleep(5 * time.Second)
close(c)
}()
fmt.Println("c阻塞在等待read")
select {
case <-c:
fmt.Println("一共阻塞了", time.Since(start))
}
}
// 随机选择的
func main02() {
c1 := make(chan interface{})
close(c1) //删掉会报错
c2 := make(chan interface{})
close(c2)
var c1Num, c2Num int
for i := 0; i < 1000; i++ {
select {
case <-c1:
c1Num++
case <-c2:
c2Num++
}
}
fmt.Println("c1:", c1Num, "c2", c2Num)
}
//default
func main03() {
start := time.Now()
var c1, c2 chan interface{}
//go func() {
// time.Sleep(5 * time.Second)
// close(c)
//}()
fmt.Println("c阻塞在等待read")
for i := 0; i < 5; i++ {
select {
case <-c1:
fmt.Println("c1")
case <-c2:
fmt.Println("c1")
default:
fmt.Println("default after ", time.Since(start))
}
}
}
// 随机选择
func main() {
done := make(chan interface{})
gotoGoOut := make(chan interface{})
count := 0
go func() {
time.Sleep(3 * time.Second)
close(done)
}()
go func() {
time.Sleep(4 * time.Second)
close(gotoGoOut)
}()
goOut:
fmt.Println("gotoGoOut")
fmt.Println(count)
loop:
for {
select {
case <-done:
break loop
case <-gotoGoOut:
goto goOut
default:
//执行某些任务
count++
time.Sleep(1 * time.Second)
}
}
}