Go 语言编程实例(二)

select 实例

Go 语言选择(select)可以等待多个通道操作。将 goroutinechannelselect 结合是 Go 语言的一个强大功能。看起来可能和 switch 相似,但是并不是。

对于这个示例,将选择两个通道。

每个通道将在一段时间后开始接收值,以模拟阻塞在并发 goroutines 中执行 RPC 操作。我们将使用 select 同时等待这两个值,在每个值达到时打印它们。

执行实例程序得到的值是 one , 然后是 two 。注意,总执行时间只有 1-2 秒 Sleeps 同时执行。

package main
import (
    "fmt"
    "time"
)

func main(){
    // channel
    c1 := make (chan string)
    c2 := make (chan string)

    go func(){
        time.Sleep(time.Second * 1)
        c1 <- "ONE"
    }()

    go func(){
        time.Sleep(time.Second * 2)
        c2 <- "TWO"
    }()

    for i := 0 ; i < 2 ; i ++ {
        select {
            case msg1 := <-c1:
            fmt.Println("received~one",msg1)
            case msg2 := <-c2:
            fmt.Println("received~two",msg2)
        }
    }
}

超时(timeouts)实例

超时对于连接到外部资源或在不需要绑定执行时间的程序很重要。在 Go 编程中由于使用了通道和选择 (select),实现超时是很容易和优雅的。

在这个示例中,假设正在执行一个外部调用,2秒后在通道 C1 上返回其结果。

这里是 select 实现超时。res := <-c1 等待结果和 <-Time 。等待在超时 1 秒后发送一个值。由于选择继续准备好第一个接收,如果超时超过允许的 1 秒,则将按照超时情况处理。

如果允许更长的超时,如: 3s ,那么从 c2 的接收将成功,这里将会打印。

运行此程序显示第一个操作超时和第二个操作超时。

使用此选择超时模式需要通过通道传达结果。这是一个好主意,因为其他重要的 Go 功能是基于渠道和 Select。现在看看下面的两个例子:计时器和ticker 。

package main
import (
    "time"
    "fmt" 
)
func main(){
    c1 := make(chan string,1)
    go func(){
        time.Sleep(time.Second *2)
        c1 <- "result 1"
    }()
    select {
        case res := <-c1:
        fmt.Println(res)
        case <-time.After(time.Second *1):
        fmt.Println("timeout 1")
    }
    c2 := make(chan string , 1)
    go func() {
        time.Sleep(time.Second * 2)
        c2 <- "result 2"
    }()
    select {
        case res := <-c2:
        fmt.Println(res)
        case <-time.After(time.Second *3):
        fmt.Println("timeout2")
    }
}

非阻塞通道操作实例

通道的基本发送和接受都阻塞。但是,可以使用 selectdefault 子句来实现非阻塞发送,接收,甚至非阻塞多路选择( select )。

这里会是一个非阻塞接收。如果消息上有可用的值,则选择将使用该值的 <-message 。如果不是,它会立即采取默认情况。

可以使用多个上面的默认子句来实现多路非阻塞选择(select)。这里尝试对消息(message) 和信号(signals) 的非阻塞接收。

package main

import (
    "fmt"
    "time"
)

func main(){
    messages := make(chan string)
    signals  := make(chan bool)

    go func() {
        messages <- "test"
    }()

    time.Sleep(time.Second * 1)
    select{
        case msg := <-messages:
            fmt.Println("Received message",msg)
        default:
            fmt.Println("no message received")
    }

    msg := "hi"

    select {
        case messages<-msg:
            fmt.Println("sent message",msg)
        default:
            fmt.Println("no message sent")
    }

    select {
        case msg := <- messages:
            fmt.Println("received message",msg)
        case sig := <-signals:
            fmt.Println("received signals",sig)
        default:
            fmt.Println("no activity")
    }
}

go 关闭通道实例

关闭通道表示不会再发送值。这对于将完成通讯到通道的接收器是很有用的。

在这个例子中,我们将使用一个作业通道来完成从 main goroutine 到 worker goroutine 的工作。当没有更多的工作时,则将关闭工作通道。

这里是工作程序 goroutine。它反复从j的工作接收 more := <- jobs 。在这种特殊的 2 值形式的接收中。如果作业已关闭并且接受到的通道中的所有值,则 more 的值将为 false。当已经完成了所有的工作时,使用这个通知。

这会通过作业通道向工作线程发送 3 个作业,然后关闭它。

package main

import "fmt"

func main(){
    jobs := make(chan int,5)
    done := make(chan bool)

    go func() {
        for {
            j , more := <- jobs
            if more{
                fmt.Println("received job",j)
            }else {
                fmt.Println("received all jobs")
                done <- true
                return
            }
        }
    }()

    for j:=1 ; j<= 3 ; j++{
        jobs <- j
        fmt.Println("send job",j)
    }

    //关闭通道
    close(jobs)
    fmt.Println("send all jobs")

    //
    fmt.Println(<-done)
}

以上例程中实际输出的时候遇到输出为以下样子:

"D:\Program\Gogland 172.3757.2\bin\runnerw.exe" C:\Go\bin\go.exe run C:/Go/MyGo/src/lesson5_select/close_chan.go
send job 1
send job 2
received job 1
received job 2
received job 3
send job 3
send all jobs
received all jobs
true

个人猜想是因为 goroutine 中执行的线程调度的时候快于 主要 routine。所以出现以上输出,但是就是实际执行过程的时候一定是接收到发送过后再输出。

go 通道范围实例

在前面的例子中,我们已经看到了 forrange 语句如何为基于数据结构提供迭代。还可以使用此语法对从通道接收的值进行迭代。

此范围在从队列中接收到的每个元素上进行迭代。因为关闭了上面的通道,迭代在接收到 2 个元素后终止。

这个示例还可以关闭非空信道,但仍接收剩余值。

package main

import "fmt"

func main(){
    //存放两个值在通道中

    queue := make(chan string,2)
    queue <- "one"
    queue <- "two"

    close(queue)

    for elem:=range queue{
        fmt.Println(elem)
    }
}

输出结果如下:

"D:\Program\Gogland 172.3757.2\bin\runnerw.exe" C:\Go\bin\go.exe run C:/Go/MyGo/src/lesson5_select/chan_range.go
one
two

go 计时器实例

我们经常想在将来某个时间点执行Go 代码,或者在某个时间间隔重复执行。 Go 内置计时器和自动接收器功能使这两项任务变得容易。我们先看看定时器,然后再看看行情。

定时器代表未来的一个事件。可告诉定时器您想要等待多长时间,它提供一个通道,得到通知的时候执行相应程序。在这个示例中,计时器将等待2 秒钟。

<-timer1.C 阻塞定时器的通道 C ,直到它发送一个指示定时器超时的值。

如果只是想等待,可以使用 time.Sleep 。定时器可能起作用的一个原因是在定时器到期之前取消定时器。

第一个定时器将在启动程序后 2s 后过期,但第二个定时器应该在它到期之前停止。

package main

import (
    "time"
    "fmt"
)

func main(){
    timer1 := time.NewTimer(time.Second * 2)

    <-timer1.C

    fmt.Println("Timer 1 expired")

    timer2 := time.NewTimer(time.Second)

    go func() {
        <- timer2.C
        fmt.Println("Timer 2 expired")
    }()

    stop2 := timer2.Stop()

    if stop2{
        fmt.Println("Timer 2 stoped")
    }
}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,335评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,895评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,766评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,918评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,042评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,169评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,219评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,976评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,393评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,711评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,876评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,562评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,193评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,903评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,699评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,764评论 2 351

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,644评论 18 139
  • Goroutine是Go里的一种轻量级线程——协程。相对线程,协程的优势就在于它非常轻量级,进行上下文切换的代价非...
    witchiman阅读 4,827评论 0 9
  • Go的内存模型 看完这篇文章你会明白 一个Go程序在启动时的执行顺序 并发的执行顺序 并发环境下如何保证数据的同步...
    初级赛亚人阅读 2,844评论 0 2
  • 微信,改界面了,标志着从“人类起源”走向“华夏文明”; 而朋友圈,经过好几轮的洗礼,也从泛泛沟通演变到了精细化经营...
    苟秋阅读 468评论 0 0
  • 渐行渐远 慢慢消失在人海 也许是你们的背影太迷人 看的我模糊了双眼 我的双眼全是你们的背影 它已深深刻在我心里 鸟...
    路上过客阅读 479评论 17 14