goroutine
并发:逻辑上具备同时处理多个任务的能力(单核,上下文切换)
并行:物理上同一时刻执行多个并发任务(多核,互不影响)
package main
import (
"fmt"
"time"
)
var c int
func counter() int {
c++
return c
}
func main() {
a := 100
go func(x, y int) {
time.Sleep(time.Second) //goroutine在main逻辑之后执行
fmt.Println("go:", x, y)
}(a, counter()) //立即计算并赋值
a += 100
fmt.Println("main", a, counter())
time.Sleep(time.Second * 3) //等goroutine结束
}
进程退出并不会等并发任务执行结束,可用channel阻塞,然后发出退出信号
package main
import (
"fmt"
"time"
)
func main() {
exit := make(chan struct{})
go func() {
time.Sleep(time.Second)
fmt.Println("goroutine done")
close(exit) //关闭通道,发出信号
}()
fmt.Println("main start...")
<-exit //通道关闭,立即解除阻塞
fmt.Println("main end...")
}
等待多个任务结束,使用sync.WaitGroup,通过设定计数器,让每个goroutine在退出前递减,直至归零时解除阻塞
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1) //累加计数
go func(id int) {
defer wg.Done() //递归计数
time.Sleep(time.Second)
fmt.Println("goroutine", id)
}(i)
}
fmt.Println("start")
wg.Wait() //阻塞,直至计数归零
fmt.Println("stop")
}
channel限制并发数
package main
import (
"fmt"
"sync"
"time"
)
var wg = sync.WaitGroup{}
func main() {
ch := make(chan int, 2)
for i := 0; i < 100; i++ {
go run(ch, i)
}
wg.Wait()
}
func run(ch chan int, i int) {
wg.Add(1)
defer wg.Done()
ch <- i
fmt.Println(i)
time.Sleep(1*time.Second)
<-ch
}
GOMAXPROCS
与逻辑核数相等
package main
import (
"fmt"
"math"
"runtime"
"sync"
)
func count() {
x := 0
for i := 0; i < math.MaxUint32; i++ {
x += i
}
fmt.Println(x)
}
func test(n int) {
for i := 0; i < n; i++ {
count()
}
}
func test2(n int) {
var wg sync.WaitGroup
wg.Add(n)
for i := 0; i < n; i++ {
go func() {
count()
wg.Done()
}()
}
wg.Wait()
}
func main() {
n := runtime.GOMAXPROCS(0)
test2(n)
}
可在多处使用Wait阻塞,它们都能收到通知
package main
import (
"fmt"
"sync"
"time"
)
func main() {
var wg sync.WaitGroup
wg.Add(1)
go func() {
wg.Wait()
fmt.Println("wait exit")
}()
go func() {
time.Sleep(time.Second)
fmt.Println("done")
wg.Done()
}()
wg.Wait()
fmt.Println("main exit")
}
多个goroutine进行通信
方式一:全局变量(加锁)
package main
import (
"fmt"
"sync"
"time"
)
var (
m = make(map[int]int)
lock sync.Mutex
)
type task struct {
n int
}
func calc(t *task) {
sum := 1
for i := 1; i <= t.n; i++ {
sum *= i
}
lock.Lock()
m[t.n] = sum
lock.Unlock()
}
func main() {
for i := 1; i < 10; i++ {
t := &task{n: i}
go calc(t)
}
time.Sleep(time.Second * 2)
lock.Lock()
for k, v := range m {
fmt.Printf("%d! is %v\n", k, v)
}
lock.Unlock()
}
用到goroutine时编译加-race竞争检测
方式二:channel
channel概念
- 类似pipe
- 先进先出
- 线程安全,多个goroutine同时访问,不需要加锁
- channel是有类型的,一个整数的channel只能存放整数
channel申明
- var 变量名 chan 类型
- var test chan int
- var test chan map[string]string
channel关闭
方法一:v,ok := <-chan
package main
import (
"fmt"
)
func main() {
ch := make(chan int, 10)
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
for {
b, ok := <-ch
if ok == false {
break
}
fmt.Println(b)
}
}
方式二:
for v := range chan
channel读写
package main
import (
"fmt"
"time"
)
func write(ch chan int) {
for i := 0; i < 100; i++ {
ch <- i
}
}
func read(ch chan int) {
var a int
for {
a = <-ch
fmt.Println("read", a)
}
}
func main() {
intChan := make(chan int, 10)
go write(intChan)
go read(intChan)
time.Sleep(time.Second)
}
判断10000内的素数
package main
import (
"fmt"
)
func calc(taskChan chan int, resChan chan int, exitChan chan bool) {
for v := range taskChan {
flag := true
for i := 2; i < v; i++ {
if v%i == 0 {
flag = false
break
}
}
if flag {
resChan <- v
}
}
exitChan <- true
}
func main() {
taskChan := make(chan int, 1000)
resChan := make(chan int, 1000)
exitChan := make(chan bool, 8)
go func() {
for i := 1; i < 10000; i++ {
taskChan <- i
}
close(taskChan)
}()
for i := 0; i < 8; i++ {
go calc(taskChan, resChan, exitChan)
}
go func() {
for i := 0; i < 8; i++ {
<-exitChan
}
close(resChan)
}()
for v := range resChan {
fmt.Println(v)
}
}
定时器
package main
import (
"fmt"
"time"
)
func main() {
t := time.NewTicker(time.Second)
for v := range t.C {
fmt.Println("hello", v)
}
t.Stop()
}
超时控制
package main
import (
"fmt"
"time"
)
func main() {
select {
case <-time.After(time.Second):
fmt.Println("timeout")
}
}

浙公网安备 33010602011771号