相信大家踏入 Go 语言的世界,肯定是被强大的并发(Concurrency)所吸引,Go 语言用最简单的关键字go就可以将任务丢到后台处理,但是开发者怎么有效率的控制并发,这是入门 Go 语言必学的技能,本章会介绍几种方式来带大家认识并发,而这三种方式分别对应到三个不同的名词:WaitGroup,Channel,及 Context。下面用简单的范例带大家了解。
10年的达坂城网站建设经验,针对设计、前端、开发、售后、文案、推广等六对一服务,响应快,48小时及时工作处理。成都全网营销的优势是能够根据用户设备显示端的尺寸不同,自动调整达坂城建站的显示方式,使网站能够适用不同显示终端,在浏览器中调整网站的宽度,无论在任何一种浏览器上浏览网站,都能展现优雅布局与设计,从而大程度地提升浏览体验。成都创新互联从事“达坂城网站设计”,“达坂城网站推广”以来,每个客户项目都认真落实执行。
WaitGroup
先来了解有什么情境需要使用到 WaitGroup,假设您有两台机器需要同时上传最新的代码,两台机器分别上传完成后,才能执行最后的重启步骤。就像是把一个工作同时拆成好几份同时一起做,可以减少时间,但是最后需要等到全部做完,才能执行下一步,这时候就需要用到 WaitGroup 才能做到。
- package main
- import (
- "fmt"
- "sync"
- )
- func main() {
- var wg sync.WaitGroup
- i := 0
- wg.Add(3) //task count wait to do
- go func() {
- defer wg.Done() // finish task1
- fmt.Println("goroutine 1 done")
- i++
- }()
- go func() {
- defer wg.Done() // finish task2
- fmt.Println("goroutine 2 done")
- i++
- }()
- go func() {
- defer wg.Done() // finish task3
- fmt.Println("goroutine 3 done")
- i++
- }()
- wg.Wait() // wait for tasks to be done
- fmt.Println("all goroutine done")
- fmt.Println(i)
- }
Channel
另外一种实际的案例就是,我们需要主动通知一个 Goroutine 进行停止的动作。换句话说,当 App 启动时,会在后台跑一些监控程序,而当整个 App 需要停止前,需要发个 Notification 给后台的监控程序,将其先停止,这时候就需要用到 Channel 来通知。看下下面这个例子:
- package main
- import (
- "fmt"
- "time"
- )
- func main() {
- exit := make(chan bool)
- go func() {
- for {
- select {
- case <-exit:
- fmt.Println("Exit")
- return
- case <-time.After(2 * time.Second):
- fmt.Println("Monitoring")
- }
- }
- }()
- time.Sleep(5 * time.Second)
- fmt.Println("Notify Exit")
- exit <- true //keep main goroutine alive
- time.Sleep(5 * time.Second)
- }
上面的例子可以发现,用了一个 Gogourtine 和 Channel 来控制。可以想像当后台有无数个 Goroutine 的时候,我们就需要用多个 Channel 才能进行控制,也许 Goroutine 内又会产生 Goroutine,开发者这时候就会发现已经无法单纯使用 Channel 来控制多个 Goroutine 了。这时候解决方式会是传递 Context。
Context
大家可以想像,今天有一个后台任务 A,A 任务又产生了 B 任务,B 任务又产生了 C 任务,也就是可以按照此模式一直产生下去,假设中途我们需要停止 A 任务,而 A 又必须告诉 B 及 C 要一起停止,这时候通过 context 方式是最快的了。
- package main
- import (
- "context"
- "fmt"
- "time"
- )
- func foo(ctx context.Context, name string) {
- go bar(ctx, name) // A calls B
- for {
- select {
- case <-ctx.Done():
- fmt.Println(name, "A Exit")
- return
- case <-time.After(1 * time.Second):
- fmt.Println(name, "A do something")
- }
- }
- }
- func bar(ctx context.Context, name string) {
- for {
- select {
- case <-ctx.Done():
- fmt.Println(name, "B Exit")
- return
- case <-time.After(2 * time.Second):
- fmt.Println(name, "B do something")
- }
- }
- }
- func main() {
- ctx, cancel := context.WithCancel(context.Background())
- go foo(ctx, "FooBar")
- fmt.Println("client release connection, need to notify A, B exit")
- time.Sleep(5 * time.Second)
- cancel() //mock client exit, and pass the signal, ctx.Done() gets the signal time.Sleep(3 * time.Second)
- time.Sleep(3 * time.Second)
- }
- package main
- import (
- "context"
- "fmt"
- "time"
- )
- func foo(ctx context.Context, name string) {
- go bar(ctx, name) // A calls B
- for {
- select {
- case <-ctx.Done():
- fmt.Println(name, "A Exit")
- return
- case <-time.After(1 * time.Second):
- fmt.Println(name, "A do something")
- }
- }
- }
- func bar(ctx context.Context, name string) {
- for {
- select {
- case <-ctx.Done():
- fmt.Println(name, "B Exit")
- return
- case <-time.After(2 * time.Second):
- fmt.Println(name, "B do something")
- }
- }
- }
- func main() {
- ctx, cancel := context.WithCancel(context.Background())
- go foo(ctx, "FooBar")
- fmt.Println("client release connection, need to notify A, B exit")
- time.Sleep(5 * time.Second)
- cancel() //mock client exit, and pass the signal, ctx.Done() gets the signal time.Sleep(3 * time.Second)
- time.Sleep(3 * time.Second)
- }
大家可以把 context 想成是一个 controller,可以随时控制不确定个数的 Goroutine,由上往下,只要宣告context.WithCancel后,再任意时间点都可以通过cancel()来停止整个后台服务。实际案例会用在当 App 需要重新启动时,要先通知全部 goroutine 停止,正常停止后,才会重新启动 App。
总结
根据不同的情境跟状况来选择不同的方式,做一个总结:
网站栏目:在 Go 语言中管理 Concurrency 的三种方式
标题链接:http://www.gawzjz.com/qtweb/news47/202547.html
网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联