Go By Example 系列:非阻塞 Channels 操作

yexiaobai · · 1002 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

注:该系列文章全部来自 Go By Example 系列翻译而来,个人翻译水平以及理解水平有限,如要更加精确的理解,请看原文Go by Example: Non-Blocking Channel Operations

在 channels (信道?) 上基本的 sends (发送) 和 receives (接收)是阻塞模式的。尽管如此, 我们可以使用 select 和一个 default 子句来非阻塞的 sends、receives,甚至是非阻塞的多路选择。

注:感谢@lidashuang的说明提醒,文章没有描述清楚,修改如下:select默认是阻塞的,但在select里面还有default语法,这类似于switch,default就是当监听的channel都没有准备好的时候,默认执行的(select不再阻塞等待channel)
同时,有时候会出现goroutine阻塞的情况,可以利用select设置超时来避免整个程序进入阻塞状态

代码版本一

代码如下:

package main
import "fmt"
func main() {
    messages := make(chan string)
    signals := make(chan bool) 

/**
这里是一个非阻塞 receive。如果在 messages 上的值是可用的,那 select 将 <-messages 的值带上,执行 <-messages 下面的println语句。如果不是,它将立即带上 default 的值,执行 default 下面的println语句
**/
    select {
    case msg := <-messages:
        fmt.Println("received message", msg)
    default:
        fmt.Println("no message received")
    }
/**
一个非阻塞 send 的类似工作 
**/
    msg := "hi"
    select {
    case messages <- msg:
        fmt.Println("sent message", msg)
    default:
        fmt.Println("no message sent")
    }

/**
我们可以用在 default 之上使用多个 cases 来实现一个非阻塞的多路 select。在这里我们尝试在 messages 和 signals 上实现非阻塞 receives。
**/

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

最后程序的结果输出为:

$ go run non-blocking-channel-operations.go 
no message received
no message sent
no activity

代码版本二

代码如下:

package main

import (
    "fmt"
)

func main() {
    // messages := make(chan string)    //如果不加缓存的话,就全部会选择defalut
    messages := make(chan string, 1) //加了缓存的话,会选择对应的
    signals := make(chan bool)

    // messages <- "test"

    select {
    case msg := <-messages:
        fmt.Println("received message", msg) //因为messages目前本身还没有值,因此选择default执行
    default:
        fmt.Println("no message received")
    }

    // go func() {
    msg := "hi world"
    // }()
    select {
    case messages <- msg:
        fmt.Println("sent message", msg) //因为channels有缓存,所以这里的msg发送到 channels messages 能处理,不会被阻塞住
    default:
        fmt.Println("no message sent")
    }

    select {
    case msg := <-messages:
        fmt.Println("received message", msg) //因为messages已经有值了,所以会选择这个case执行
    case sig := <-signals:
        fmt.Println("received signal", sig)
    default:
        fmt.Println("no activity")
    }
}

输出结果如下:

$ go run non-blocking-channel-operations.go 
no message received
sent message hi world
received message hi world   

代码版本三

代码如下:

package main

import (
    "fmt"
)

func main() {
    // messages := make(chan string)    //如果不加缓存的话,就全部会选择defalut
    messages := make(chan string, 1) //加了缓存的话,会选择对应的
    signals := make(chan bool)

    messages <- "test"

    select {
    case msg := <-messages:
        fmt.Println("received message", msg) //messages已经有值,因此选择这个case执行
    default:
        fmt.Println("no message received")
    }

    // go func() {
    msg := "hi world"
    // }()
    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 signal", sig)
    default:
        fmt.Println("no activity")
    }
}


代码输出结果如下:

$ go run non-blocking-channel-operations.go 
received message test
sent message hi world
received message hi world

代码版本四

代码如下:

package main

import (
    "fmt"
)

func main() {
    messages := make(chan string) //如果不加缓存的话,就全部会选择defalut
    //messages := make(chan string, 1) //加了缓存的话,会选择对应的
    signals := make(chan bool)

    messages <- "test" //因为该channels没有缓存,而其又赋值了,会到时死锁。

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

    // go func() {
    msg := "hi world"
    // }()
    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 signal", sig)
    default:
        fmt.Println("no activity")
    }
}

代码输出结果为:

fatal error: all goroutines are asleep - deadlock!

runtime goroutine

runtime包中有几个处理goroutine的函数

  1. Goexit - 退出当前执行的goroutine,但是defer函数还会继续调用
  2. Gosched - 让出当前goroutine的执行权限,调度器安排其它等待的任务运行,并在下次某个时候从该位置恢复执行
  3. NumCPU - 返回CPU核数量
  4. NumGoroutine - 返回正在执行和排队的任务总数
  5. GOMAXPROCS - 用来设置可以运行的CPU核数

有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:yexiaobai

查看原文:Go By Example 系列:非阻塞 Channels 操作

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

1002 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传