相信大家对线程
并不陌生,顾名思义就是同时做多件事情,线程
对于编程语言的重要性不言而喻。在开发App时经常需要开启线程
去处理一些耗时的操作,在做服务器开发也是需要开启多线程去处理多个客户端的请求。当时众所周知线程
是属于系统层面上的东西,开启线程会耗费很多资源且不易管理。
Goroutine
是 Go语言
面向线程的轻量级方法( 注: Goroutine
不是线程 ),创建Goroutine
的成本很低,只需要几千个字节的额外内存,这使同时运行成千上万个Goroutine
成为可能。
Goroutine 也 非常的容易上手,只需要一行 go func(...)
就可以了。下面我们来实践一下,创建一个简单的Goroutine
package main
import (
"fmt"
)
func RunLoop(){
for i := 0; i < 10; i++ {
fmt.Printf("%d ", i)
}
}
func main(){
go func() { //开启goroutine
RunLoop()
}()
RunLoop()
}
打印输出:
0 1 2 3 4 5 6 7 8 9
goroutine
还没有开始执行,程序就结束了。导致只打印了一次0~9。
这时我们可以用WaitGroup
来同步goroutine
,WaitGroup
等待所有任务退出主程序再退出,在每个goroutine
完成后需调用Done()
。
package main
import (
"fmt"
"sync"
)
func RunLoop(){
for i := 0; i < 10; i++ {
fmt.Printf("%d ", i)
}
}
func main(){
var wg sync.WaitGroup
wg.Add(1)
go func() { //开启goroutine
defer wg.Done()
RunLoop()
}()
RunLoop()
wg.Wait()
}
这样子就可以打印正常了
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
资源竞争
在并发的过程中就会涉及到资源竞争的问题,多个并发操作同一个资源从而导致没有给出正确的结果。下面直接上代码
package main
import (
"fmt"
"sync"
"runtime"
)
var counter int
var wg sync.WaitGroup
func CounterAdd(){
defer wg.Done()
for i := 0; i < 2; i++ {
value := counter
//退出当前goroutine,并放回队列中
runtime.Gosched()
value ++
counter = value
}
}
func main(){
wg.Add(2)
go CounterAdd()
go CounterAdd()
wg.Wait()
fmt.Println("Counter:", counter)
}
这时就产生了资源竞争的问题,输出结果有可能会输出 2、3、4
,但是只有4这个结果才是正确的。
Go语言
中有一个工具(-race
),可以用来检测竞争状态,因为有时候竞争状态看起来并不那么明显。
go run -race goroutine.go
WARNING: DATA RACE
Read at 0x000001211840 by goroutine 7:
main.CounterAdd()
/Users/justin910/Documents/Go/练习/goroutine.go:18 +0x6f
Previous write at 0x000001211840 by goroutine 6:
main.CounterAdd()
/Users/justin910/Documents/Go/练习/goroutine.go:25 +0x90
Goroutine 7 (running) created at:
main.main()
/Users/justin910/Documents/Go/练习/goroutine.go:34 +0x77
Goroutine 6 (finished) created at:
main.main()
/Users/justin910/Documents/Go/练习/goroutine.go:33 +0x5f
==================
Counter: 4
Found 1 data race(s)
exit status 66
根据上面的错误提示,可以查到是哪个goroutine
引发了数据竞争,以及那几行代码有冲突。
sync.Mutex互斥锁
我们可以用系统sync
包中提供的互斥锁
来解决这一问题,保证同一时间最多只有一个goroutine
访问该共享资源。
package main
import (
"fmt"
"sync"
"runtime"
)
var counter int
var wg sync.WaitGroup
var mutex sync.Mutex
func CounterAdd(){
defer wg.Done()
for i := 0; i < 2; i++ {
mutex.Lock()
value := counter
//退出当前goroutine,并放回队列中
runtime.Gosched()
value ++
counter = value
mutex.Unlock()
}
}
func main(){
wg.Add(2)
go CounterAdd()
go CounterAdd()
wg.Wait()
fmt.Println("Counter:", counter)
}
加上互斥锁
以后就能有效的保证共享资源的正确性。
总结
以上就是对于Go语言
中goroutine
的介绍和使用,欢迎大家指出不足,相互交流。
有疑问加站长微信联系(非本文作者)