package main
import (
"fmt"
"time"
)
func hello() {
fmt.Println("Hello Goroutine!")
}
func main() {
// 直接调用 可以执行hello方法
// hello()
// 增加关键词 go 有概率不执行hello方法结束
// 在程序启动时,Go程序就会main()函数创建一个默认的goroutine,默认结束,其他goroutine一同结束
go hello()
fmt.Println("main goroutine done!")
// 最简单的方式就添加time.Sleep()
time.Sleep(time.Second)
}
package main
import (
"fmt"
"sync"
)
// sync.WaitGroup来实现goroutine的同步
var wg sync.WaitGroup
func hello(i int) {
defer wg.Done()
fmt.Println("hello,Goroutine", i)
}
func main() {
for i := 0; i < 10; i++ {
// 启动一个goroutine就登记一个
wg.Add(1)
// 10个goroutine是并发执行的,而goroutine的调度是随机的
go hello(i)
}
//等待所有登记的goroutine都结束
wg.Wait()
}
package main
import (
"fmt"
"runtime"
)
func main() {
go func(s string) {
for i := 0; i < 2; i++ {
fmt.Println(s)
}
}("world")
// 主协程
for i := 0; i < 2; i++ {
// 切一下 ,再次分配任务
runtime.Gosched()
fmt.Println("hello")
}
}
package main
import (
"fmt"
"runtime"
)
func main() {
go func() {
defer fmt.Println("A.defer")
func() {
defer fmt.Println("B.defer")
// 结束协程
runtime.Goexit()
defer fmt.Println("C.defer")
fmt.Println("B")
}()
}()
for {
}
}
package main
import (
"fmt"
"runtime"
"time"
)
func a() {
for i := 1; i < 10; i++ {
fmt.Println("A:", i)
}
}
func b() {
for i := 1; i < 10; i++ {
fmt.Println("B:", i)
}
}
func main() {
runtime.GOMAXPROCS(8)
go a()
go b()
time.Sleep(time.Second)
}
var 变量 chan 元素类型
var ch chan int
fmt.Println(ch)//nil
make(chan 元素类型,[缓冲大小])
ch1 := make(chan int)
//初始化通道
ch := make(chan int)
ch<-10
// 从ch中接收值并赋值给变量x
x := <- ch
//从ch中接收者,忽略结果
<-ch
close(ch)
// 编译能通过,但是不能执行,报 deadlock
ch := make(chan int)
ch <- 10
func recv(c chan int){
ret <- c
fmt.Println("接收到的值",ret)
}
func main(){
ch := make(chan int)
//启用goroutine从通道接收值
go recv()
ch <- 10
fmt.Println("发送成功")
}
package main
import "fmt"
func main() {
//创建一个容量为1的有缓冲区通道
ch := make(chan int, 1)
ch <- 10
fmt.Println("发送成功")
close(ch)
}
package main
import "fmt"
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
// 开启goroutine将0~99的数发送到ch1
go func() {
for i := 0; i < 100; i++ {
ch1 <- i
}
close(ch1)
}()
//开启goroutine从ch1中接收值,并将该值的平方发送到ch2中
go func() {
for {
// 通道关闭后再取值 ok = false
i, ok := <-ch1
if !ok {
break
}
ch2 <- i * i
}
close(ch2)
}()
for i := range ch2 {
fmt.Println(i)
}
}
package main
import "fmt"
func counter(out chan<- int) {
for i := 0; i < 100; i++ {
out <- i
}
close(out)
}
func squarer(out chan<- int, in <-chan int) {
for i := range in {
out <- i * i
}
close(out)
}
func printer(in <-chan int) {
for i := range in {
fmt.Println(i)
}
}
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
go counter(ch1)
go squarer(ch2, ch1)
printer(ch2)
}
channel | nil | 非空 | 空的 | 满了 | 没满 |
---|---|---|---|---|---|
接收 | 阻塞 | 接收值 | 阻塞 | 接收值 | 接收值 |
发送 | 阻塞 | 发送值 | 发送值 | 阻塞 | 发送值 |
关闭 | panic | 关闭成功,读完数据后返回默认值 | 关闭成功,返回默认值 | 关闭成功,读完数据后返回默认值 | 关闭成功,读完数据后返回默认值 |
package main
import (
"fmt"
"math/rand"
)
type Job struct {
Id int
RandNum int
}
type Result struct {
job *Job
sum int
}
// 创建工作池
// 参数1:开几个协程
func createPool(num int, jobChan chan *Job, resultChan chan *Result) {
//根据开协程个数,去运行
for i := 0; i < num; i++ {
go func(jobChan chan *Job, resultChan chan *Result) {
//执行运算
//遍历job通道所有数据,数字进行相加
for job := range jobChan {
//随机数接过来
r_num := job.RandNum
//随机数每一位相加
//定义返回值(结果)
var sum int
for r_num != 0 {
tmp := r_num % 10
sum += tmp
r_num /= 10
}
//想要得结果result
r := &Result{
job: job,
sum: sum,
}
//运算结果放到管道
resultChan <- r
}
}(jobChan, resultChan)
}
}
func main() {
//创建两个通道
// job通道
jobChan := make(chan *Job, 128)
// 结果通道
resultChan := make(chan *Result, 128)
//创建工作池
createPool(64, jobChan, resultChan)
go func(resultChan chan *Result) {
//遍历管道
for result := range resultChan {
fmt.Printf("job id:%v randnum:%v result:%d\n", result.job.Id, result.job.RandNum, result.sum)
}
}(resultChan)
var id int
// 循环创建job,输入到管道
for {
id++
//生成随机数
r_num := rand.Int()
job := &Job{
Id: id,
RandNum: r_num,
}
jobChan <- job
}
}
package main
import (
"fmt"
"time"
)
func main() {
// timer 基本使用
// timer1 := time.NewTimer(2 * time.Second)
// t1 := time.Now()
// fmt.Printf("t1:%v\n", t1)
// t2 := <-timer1.C
// fmt.Printf("t2:%v\n", t2)
// 验证timer只能响应一次
// timer2 := time.NewTimer(time.Second)
// for {
// <-timer2.C
// fmt.Println("时间到")
// }
// timer 实现延时的功能
// time.Sleep(time.Second)
// timer3 := time.NewTimer(2 * time.Second)
// <-timer3.C
// fmt.Println("时间到")
// <-time.After(2 * time.Second)
// fmt.Println("2秒到")
// 停止定时器
// timer4 := time.NewTimer(2 * time.Second)
// go func() {
// <-timer4.C
// fmt.Println("定时器执行了")
// }()
// b := timer4.Stop()
// if b {
// fmt.Println("timer4 已经关闭")
// }
// 重置定时器
timer5 := time.NewTimer(3 * time.Second)
timer5.Reset(1 * time.Second)
fmt.Println(time.Now())
fmt.Println(<-timer5.C)
for {
}
}
package main
import (
"fmt"
"time"
)
func main() {
//获取ticker对象
ticker := time.NewTicker(1 * time.Second)
i := 0
//子协程
go func() {
for {
//调用一次 <-ticker.C 就延迟1s
//<-ticker.C
i++
fmt.Println(<-ticker.C)
if i == 5 {
//stop
ticker.Stop()
}
}
}()
for {
}
}
for{
//尝试从ch1接收值
data,ok := <-ch1
// 尝试从ch2接收值
data,ok := <-ch2
}
select{
case <-ch1:
//如果ch1成功读到数据,则进行该case处理语句
case ch2 <-2
//如果成功向ch2写入数据,则进行该case处理语句
default:
//如果上面都没成功,则进入default处理流程
}
package main
import (
"fmt"
"time"
)
func test1(ch chan int) {
time.Sleep(time.Second * 5)
ch <- 1
}
func test2(ch chan int) {
time.Sleep(time.Second * 2)
ch <- 2
}
func main() {
ch1 := make(chan int)
ch2 := make(chan int)
//select 可以同时监听一个或多个channel,直到其中一个channel ready
go test1(ch1)
go test2(ch2)
//用select监控 执行了case data2 := <-ch2
select {
case data1 := <-ch1:
fmt.Println("data1: ", data1)
case data2 := <-ch2:
fmt.Println("data2: ", data2)
}
}
//创建两个管道
int_chan := make(chan int, 1)
string_chan := make(chan string, 1)
go func() {
// 协程线程休眠2s
// time.Sleep(time.Second * 2)
int_chan <- 1
}()
go func() {
string_chan <- "hello"
}()
select {
case value := <-int_chan:
fmt.Println("int: ", value)
case value := <-string_chan:
fmt.Println("string: ", value)
}
fmt.Println("main结束")
//创建管道
ch := make(chan string, 10)
//子协程写数据
go write(ch)
//取数据
for str := range ch {
fmt.Println("str: ", str)
time.Sleep(time.Second)
}
func write(ch chan<- string) {
for {
select {
//写数据
case ch <- "hello":
fmt.Println("write hello")
default:
fmt.Println("channel full")
}
time.Sleep(time.Millisecond * 500)
}
}
package main
import (
"fmt"
"sync"
)
var x int64
var wg sync.WaitGroup
func add() {
for i := 0; i < 5000; i++ {
x = x + 1
}
wg.Done()
}
func main() {
wg.Add(2)
go add()
go add()
wg.Wait()
fmt.Println("x: ", x)
}
package main
import (
"fmt"
"sync"
)
var x int64
var wg sync.WaitGroup
var lock sync.Mutex
func add() {
for i := 0; i < 5000; i++ {
//加锁
lock.Lock()
x = x + 1
//解锁
lock.Unlock()
}
wg.Done()
}
func main() {
wg.Add(2)
go add()
go add()
wg.Wait()
fmt.Println("x: ", x)
}
互斥锁是完全互斥的,但是有很多实际的场景下是读多写少的,当我们并发的去读取一个资源不涉及资源修改的时候是没有必要加锁的,这种场景下使用读写锁是更好的一种选择。读写锁在Go语言中使用sync包中的RWMutex类型
读写锁分为两种:读锁和写锁
package main
import (
"fmt"
"sync"
"time"
)
var (
x int64
wg sync.WaitGroup
lock sync.Mutex
rwlock sync.RWMutex
)
func write() {
// 加互斥锁
//lock.Lock()
// 加写锁
rwlock.Lock()
x = x + 1
time.Sleep(time.Millisecond * 10)
// 解写锁
rwlock.Unlock()
// 解互斥锁
// lock.Unlock()
wg.Done()
}
func read() {
// 加互斥锁
//lock.Lock()
// 加读锁
rwlock.RLock()
time.Sleep(time.Millisecond)
//解读锁
rwlock.RUnlock()
// 解互斥锁
//lock.Unlock()
wg.Done()
}
func main() {
start := time.Now()
for i := 0; i < 10; i++ {
wg.Add(1)
go write()
}
for i := 0; i < 1000; i++ {
wg.Add(1)
go read()
}
wg.Wait()
end := time.Now()
fmt.Println(end.Sub(start))
}
方法名 | 功能 |
---|---|
(wg * WaitGroup) Add(delta int) | 计数器+delta |
(wg *WaitGroup) Done() | 计数器-1 |
(wg *WaitGroup) Wait() | 阻塞直到计数器变为0 |
package main
import (
"fmt"
"sync"
)
var wg sync.WaitGroup
func hello() {
defer wg.Done()
fmt.Println("Hello Goroutine")
}
func main() {
wg.Add(1)
// 启动另外一个goroutine去执行hello函数
go hello()
fmt.Println("main goroutine done")
wg.Wait()
}
func (o * Once)Do(f func()){}
type Once struct{
// done 字段用来判断某行为action是否已进行,因为hot path中被使用,放在结构体的第一字段能够减少机器指令
done uint32
m Mutex
}
func (o *Once) Do(f func()) {
// 原子加载标识值,判断是否已被执行过
if atomic.LoadUint32(&o.done) == 0 {
o.doSlow(f)
}
}
func (o *Once) doSlow(f func()) { // 还没执行过函数
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 { // 再次判断下是否已被执行过函数
/*
*Once 本身的语义就是对外保证你传进来 f 执行过一次,若 f 在执行过程中 panic 了,会导致 Do 也直接退出,但是退出前会把所有的 defer 都执行完,保证了 f 执行过一次。若放在 f() 后面,当 f 发生 panic 之后,done 就不能置为 1
*/
defer atomic.StoreUint32(&o.done, 1) // 原子操作:修改标识值
f() // 执行函数
}
}
package main
import (
"fmt"
"strconv"
"sync"
)
var m = make(map[string]int)
func get(key string) int {
return m[key]
}
func set(key string, value int) {
m[key] = value
}
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 20; i++ {
wg.Add(1)
go func(n int) {
key := strconv.Itoa(n)
set(key, n)
fmt.Printf("k=%v,v=%v\n", key, get(key))
wg.Done()
}(i)
wg.Wait()
}
}
var m = sync.Map{}
func main() {
wg := sync.WaitGroup{}
for i := 0; i < 20; i++ {
wg.Add(1)
go func(n int) {
key := strconv.Itoa(n)
m.Store(key, n)
value, _ := m.Load(key)
fmt.Printf("k=:%v,v:=%v\n", key, value)
wg.Done()
}(i)
}
wg.Wait()
}
package main
import (
"fmt"
"sync"
"sync/atomic"
"time"
)
var x int64
var l sync.Mutex
var wg sync.WaitGroup
// 普通版函数
func add() {
x++
wg.Done()
}
// 互斥锁版函数
func mutesAdd() {
l.Lock()
x++
l.Unlock()
wg.Done()
}
// 原子操作版函数
func atomicAdd() {
atomic.AddInt64(&x, 1)
wg.Done()
}
func main() {
start := time.Now()
for i := 0; i < 1000000; i++ {
wg.Add(1)
// 普通版add函数 不是并发安全的
// 执行时间 3ms左右 值不固定
//go add()
//加锁版add函数,是并发安全的,但是加锁性能开销大
// 执行时间 2~5ms 值固定
//go mutesAdd()
// 原子操作版add函数 是并发安全,性能优于加锁版
// 执行时间 2~5ms 值固定
go atomicAdd()
}
wg.Wait()
end := time.Now()
fmt.Println("x=", x)
fmt.Println(end.Sub(start))
}