解锁即可观看《Go开发工程师》完整课程视频

Go开发工程师

未来3-5年企业高性能项目不可替代的语言,从基础到项目实战再到重构,真正从入门到精通

【第1周】Go基础知识入门
【第2周】容器,go编程思想
【第3周】Go并发编程和工程管理
【第4周】从0开始理解rpc和grpc
【第5周】grpc和protobuf进阶
【第6周】 yapi文档管理、gorm详解
【第7周】gin快速入门
【第8周】用户服务的grpc服务
【第9周】用户服务的web服务
【第10周】服务注册/发现、配置中心、负载均衡
【第11周】商品微服务的grpc服务
【第12周】 商品微服务的gin层和oss图片服务
【第13周】库存服务和分布式锁
【第14周】订单和购物车微服务
【第15周】 支付宝支付、用户操作微服务、前后端联调
【第16周】elasticsearch实现搜索微服务
【第17周】 分布式理论基础、分布式事务解决方案
【第18周】 学习rocketmq实现幂等性机制等
【第19周】链路追踪、限流、熔断、降级
【第20周】api网关、部署
【第21周】开发规范和go基础扩展
【第22周】设计模式和单元测试
【第23周】protoc插件开发、cobra命令行
【第24周】log日志包设计
【第25周】ast代码生成工具开发
【第26周】三层代码结构
【第27周】grpc服务封装更方便的rpc服务
【第28周】深入grpc的服务注册、负载均衡原理
【第29周】基于gin封装api服务
【第30周】可观测的终极解决方案
【第31周】系统监控核心
【第32周】用户、商品服务重构
【第33周】订单、库存等服务重构
【第33+周】订单服务重构、wire进行ioc控制
【第34周】通过k8s部署服务
【第34+周】devops和k8s
章节
问答
课签
笔记
评论
占位
占位

go语句初探

    go语句和通道类型是Go语言的并发编程理念的最终体现。在第五章,我已经详细介绍过了通道类型。相比之下,go语句在用法上要比通道简单很多。与defer语句相同,go语句也可以携带一条表达式语句。注意,go语句的执行会很快结束,并不会对当前流程的进行造成阻塞或明显的延迟。一个简单的示例如下:

go fmt.Println("Go!")

    可以看到,go语句仅由一个关键字go和一条表达式语句构成。同样的,go语句的执行与其携带的表达式语句的执行在时间上没有必然联系。这里能够确定的仅仅是后者会在前者完成之后发生。在go语句被执行时,其携带的函数(也被称为go函数)以及要传给它的若干参数(如果有的话)会被封装成一个实体(即Goroutine),并被放入到相应的待运行队列中。Go语言的运行时系统会适时的从队列中取出待运行的Goroutine并执行相应的函数调用操作。注意,对传递给这里的函数的那些参数的求值会在go语句被执行时进行。这一点也是与defer语句类似的。
  
    正是由于go函数的执行时间的不确定性,所以Go语言提供了很多方法来帮助我们协调它们的执行。其中最简单粗暴的方法就是调用time.Sleep函数。请看下面的示例:

package main

import (
    "fmt"
)

func main() {
    go fmt.Println("Go!")
}  

    这样一个命令源码文件被运行时,标准输出上不会有任何内容出现。因为还没等Go语言运行时系统调度那个go函数执行,主函数main就已经执行完毕了。函数main的执行完毕意味着整个程序的执行的结束。因此,这个go函数根本就没有执行的机会。
  
  但是,当我们在上述go语句的后面添加一条对time.Sleep函数的调用语句之后情况就会不同了:

package main

import (
    "fmt"
    "time"
)

func main() {
    go fmt.Println("Go!")
    time.Sleep(100 * time.Millisecond)
}

    语句time.Sleep(100 * time.Millisecond)会把main函数的执行结束时间向后延迟100毫秒。100毫秒虽短暂,但足够go函数被调度执行的了。上述命令源码文件在被运行时会如我们所愿地在标准输出上打印出Go!

  
    另一个比较绅士的做法是在main函数的最后调用runtime.Gosched函数。相应的程序版本如下:

package main

import (
    "fmt"
    "runtime"
)

func main() {
    go fmt.Println("Go!")
    runtime.Gosched()
}

   runtime.Gosched函数的作用是让当前正在运行的Goroutine(这里是运行main函数的那个Goroutine)暂时“休息”一下,而让Go运行时系统转去运行其它的Goroutine(这里是与go fmt.Println("Go!")对应并会封装fmt.Println("Go!")的那个Goroutine)。如此一来,我们就更加精细地控制了对几个Goroutine的运行的调度。
  
    当然,我们还有其它方法可以满足上述需求。并且,如果我们需要去左右更多的Goroutine的运行时机的话,下面这种方法也许更合适一些。请看代码:

package main

import (
    "fmt"
    "sync"
)

func main() {
    var wg sync.WaitGroup
    wg.Add(3)
    go func() {
        fmt.Println("Go!")
        wg.Done()
    }()
    go func() {
        fmt.Println("Go!")
        wg.Done()
    }()
    go func() {
        fmt.Println("Go!")
        wg.Done()
    }()
    wg.Wait()
}

    sync.WaitGroup类型有三个方法可用——AddDoneWaitAdd会使其所属值的一个内置整数得到相应增加,Done会使那个整数减1,而Wait方法会使当前Goroutine(这里是运行main函数的那个Goroutine)阻塞直到那个整数为0。这下你应该明白上面这个示例所采用的方法了。我们在main函数中启用了三个Goroutine来封装三个go函数。每个匿名函数的最后都调用了wg.Done方法,并以此表达当前的go函数会立即执行结束的情况。当这三个go函数都调用过wg.Done函数之后,处于main函数最后的那条wg.Wait()语句的阻塞作用将会消失,main函数的执行将立即结束。
  
    与go语句、go函数以及承载其运行的Goroutine相关的话题其实还有很多。不过由于篇幅等原因,我的讲述就先到此为止。如果大家对这方面的内容感兴趣的话请参看《Go并发编程实战》这本书。这里面会有非常详尽的介绍。

任务

    在命令源码文件index.go中的main函数中,我编写了三条go语句。其中的go函数会分别向标准输出打印一个整数。我希望打印的最终结果是

1
2
3

    请你对其中的代码做适当的修改来满足这个需求。

?不会了怎么办

修改后的文件内容可以是:

package main

import (
    "fmt"
)

func main() {
    ch1 := make(chan int, 1)
    ch2 := make(chan int, 1)
    ch3 := make(chan int, 3)
    go func() {
        fmt.Println("1")
        ch1 <- 1
    }()
    go func() {
        <-ch1
        fmt.Println("2")
        ch2 <- 2
    }()
    go func() {
        <-ch2
        fmt.Println("3")
        ch3 <- 3
    }()
    <-ch3
}

 

||

提问题

写笔记

公开笔记
提交
||

请验证,完成请求

由于请求次数过多,请先验证,完成再次请求

加群二维码

打开微信扫码自动绑定

您还未绑定服务号

绑定后可得到

  • · 粉丝专属优惠福利
  • · 大咖直播交流干货
  • · 课程更新,问题答复提醒
  • · 账号支付安全提醒

收藏课程后,能更快找到我哦~

使用 Ctrl+D 可将课程添加到书签

邀请您关注公众号
关注后,及时获悉本课程动态

举报

0/150
提交
取消
全部 精华 我要发布
全部 我要发布
最热 最新
只看我的

手记推荐

更多

本次提问将花费2个积分

你的积分不足,无法发表

为什么扣积分?

本次提问将花费2个积分

继续发表请点击 "确定"

为什么扣积分?