协程

Go 协程 在执行上来说是轻量级的线程。

代码演示

import (
"fmt"
"time"
) func f(from string) {
for i := 0; i < 3; i++ {
fmt.Println(from, ":", i)
time.Sleep(100)
}
} func main() { // 假设我们有一个函数叫做 f(s)。我们使用一般的方式调并同时运行。
f("direct") // 使用 go f(s) 在一个 Go 协程中调用这个函数。这个新的 Go 协程将会并行的执行这个函数调用。
go f("goroutine") // 可以为匿名函数启动一个 Go 协程。
go func(msg string) {
for i := 0; i < 3; i++ {
fmt.Println(msg, "-", i)
time.Sleep(100)
}
}("going") // 现在这两个 Go 协程在独立的 Go 协程中异步的运行,所以我们需要等它们执行结束。
// 这里的 Scanln 代码需要我们在程序退出前按下任意键结束。
var input string
fmt.Scanln(&input)
fmt.Println("done:" + input)
}

输出:

direct : 0
direct : 1
direct : 2
going - 0
goroutine : 0
goroutine : 1
going - 1
going - 2
goroutine : 2
done:my

当我们运行这个程序时,将首先看到阻塞式调用的输出,然后是两个 Go 协程的交替输出。

这种交替的情况表示 Go 运行时是以异步的方式运行协程的。

通道

通道 是连接多个 Go 协程的管道。你可以从一个 Go 协程将值发送到通道,然后在别的 Go 协程中接收。

func main() {
// 使用 make(chan val-type) 创建一个新的通道。通道类型就是他们需要传递值的类型。
messages := make(chan string) // 使用 channel <- 语法 发送 一个新的值到通道中。
// 这里我们在一个新的 Go 协程中发送 "ping" 到上面创建的messages 通道中。
go func() {
messages <- "ping1"
messages <- "ping2"
}() // 使用 <-channel 语法从通道中 接收 一个值。
// 这里将接收我们在上面发送的 "ping" 消息并打印出来。
msg1 := <-messages
fmt.Println(msg1) fmt.Println(<-messages)
fmt.Println(<-messages)
}

输出

ping1
ping2
fatal error: all goroutines are asleep - deadlock!

我们运行程序时,通过通道,消息 "ping" 成功的从一个 Go 协程传到另一个中。

默认发送和接收操作是阻塞的,直到发送方和接收方都准备完毕。这个特性允许我们,不使用任何其它的同步操作,来在程序结尾等待消息 "ping"。

通道缓冲

默认通道是 无缓冲 的,这意味着只有在对应的接收(<- chan)通道准备好接收时,才允许进行发送(chan <-)。

可缓存通道允许在没有对应接收方的情况下,缓存限定数量的值。

func main() {
// 这里我们 make 了一个通道,最多允许缓存 3 个值。
messages := make(chan string, 3) // 因为这个通道是有缓冲区的,即使没有一个对应的并发接收方,我们仍然可以发送这些值。
messages <- "ping1"
messages <- "ping2"
messages <- "ping3" fmt.Println(<-messages)
fmt.Println(<-messages)
fmt.Println(<-messages)
}

输出

ping1
ping2
ping3

通道同步

我们可以使用通道来同步 Go 协程间的执行状态。这里是一个使用阻塞的接受方式来等待一个 Go 协程的运行结束。

// 这是一个我们将要在 Go 协程中运行的函数。
// done 通道将被用于通知其他 Go 协程这个函数已经工作完毕。
func worker(done chan bool) {
fmt.Print("working...")
time.Sleep(time.Second)
fmt.Println("done") // 发送一个值来通知我们已经完工啦。
done <- true
} func main() {
// 运行一个 worker Go协程,并给予用于通知的通道。
done := make(chan bool, 1)
go worker(done) //程序将在接收到通道中 worker 发出的通知前一直阻塞。
<-done
}

输出:

working...done

如果你把 <- done 这行代码从程序中移除,程序甚至会在 worker还没开始运行时就结束了。

通道方向

当使用通道作为函数的参数时,你可以指定这个通道是不是只用来发送或者接收值。这个特性提升了程序的类型安全性。

// ping 函数定义了一个只允许发送数据的通道。尝试使用这个通道来接收数据将会得到一个编译时错误。
func ping(pings chan<- string, msg string) {
pings <- msg
} // pong 函数允许通道(pings)来接收数据,另一通道(pongs)来发送数据。
func pong(pings <-chan string, pongs chan<- string) {
msg := <-pings
pongs <- msg
} func main() {
pings := make(chan string, 1)
pongs := make(chan string, 1)
ping(pings, "passed message")
pong(pings, pongs)
fmt.Println(<-pongs)
}

输出:

passed message

通道选择器

Go 的通道选择器 让你可以同时等待多个通道操作。

Go 协程和通道以及选择器的结合是 Go 的一个强大特性。

import (
"fmt"
"time"
) func main() {
// 在我们的例子中,我们将从两个通道中选择。
c1 := make(chan string)
c2 := make(chan string) // 各个通道将在若干时间后接收一个值,这个用来模拟例如并行的 Go 协程中阻塞的 RPC 操作
go func() {
time.Sleep(time.Second * 1)
c1 <- "one"
}()
go func() {
time.Sleep(time.Second * 2)
c2 <- "two"
}() // 我们使用 select 关键字来同时等待这两个值,并打印各自接收到的值。
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received", msg1)
case msg2 := <-c2:
fmt.Println("received", msg2)
}
}
}

输出:

received one
received two

我们首先接收到值 "one",然后就是预料中的 "two"了。

注意从第一次和第二次 Sleeps 并发执行,总共仅运行了两秒左右。

[Golang]-5 协程、通道及其缓冲、同步、方向和选择器的更多相关文章

  1. GoLang之协程

    GoLang之协程 目前,WebServer几种主流的并发模型: 多线程,每个线程一次处理一个请求,在当前请求处理完成之前不会接收其它请求:但在高并发环境下,多线程的开销比较大: 基于回调的异步IO, ...

  2. Golang 之协程详解

    转自:https://www.cnblogs.com/liang1101/p/7285955.html 一.Golang 线程和协程的区别 备注:需要区分进程.线程(内核级线程).协程(用户级线程)三 ...

  3. 在C++中使用golang的协程

    开源项目cpp_features提供了一个仿golang协程的stackful协程库. 可以在c++中使用golang的协程,大概语法是这样的: #include <iostream> v ...

  4. Swoole协程与传统fpm同步模式比较

    如果说数组是 PHP 的精髓,数组玩得不6的,根本不能算是会用PHP.那协程对于 Swoole 也是同理,不理解协程去用 Swoole,那就是在瞎用. 首先,Swoole 只能运行在命令行(Cli)模 ...

  5. swoole协程通道channel

    swoole 协程通道 为了协程直接互相通讯传递数据 和go的通道很相似 Co\run(function(){ $chan = new Swoole\Coroutine\Channel(1); Swo ...

  6. 『GoLang』协程与通道

    作为一门 21 世纪的语言,Go 原生支持应用之间的通信(网络,客户端和服务端,分布式计算)和程序的并发.程序可以在不同的处理器和计算机上同时执行不同的代码段.Go 语言为构建并发程序的基本代码块是 ...

  7. golang协程——通道channel阻塞

    新的一年开始了,不管今天以前发生了什么,向前看,就够了. 说到channel,就一定要说一说线程了.任何实际项目,无论大小,并发是必然存在的.并发的存在,就涉及到线程通信.在当下的开发语言中,线程通讯 ...

  8. golang:协程安全

    多路复用 Go语言中提供了一个关键字select,通过select可以监听channel上的数据流动.select的用法与switch语法类似,由select开始一个新的选择块,每个选择条件由case ...

  9. Golang 的 协程调度机制 与 GOMAXPROCS 性能调优

    作者:林冠宏 / 指尖下的幽灵 掘金:https://juejin.im/user/587f0dfe128fe100570ce2d8 博客:http://www.cnblogs.com/linguan ...

随机推荐

  1. 【Shell】使用awk sed获取一行内容的两个值

    突然有需求需要一个脚本,同时获取到每一行数据的两个值,下面做了一个例子模板,仅供记录参考 cat test.txt  id=1,name=zclinux1 id=2,name=zclinux2 id= ...

  2. 【Docker】runtime create failed: container_linux.go:345: 解决

    ------------------------------------------------------------------------------------------------- | ...

  3. 【葵花宝典】kolla部署OpenStack-AllinOne

    1.关闭防火墙以及内核安全机制 systemctl stop firewalld systemctl disable firewalld ##永久性关闭 setenforce 0 sed -i 's/ ...

  4. JVM(三)从JVM源码角度看类加载器层级关系和双亲委派

    类加载器我们都知道有如下的继承结构,这个关系只是逻辑上的父子关系. 我们一直听说引导类加载器没有实体,为什么没有实体呢? 因为引导类加载器只是一段C++代码并不是什么实体类,所谓的引导类加载器就是那一 ...

  5. 转 3 jmeter的两种录制方法

      录制1-badboy(推荐) badboy是一款自动化测试工具,它可以完成简单的功能测试和性能测试.其实它是一款独立的测试工具,只不过它录制东西导出的格式适用于jmeter,所以我们经常把jmet ...

  6. 华为路由配置IPSec

    用该方法配置后用抓包工具抓取的就看不到两个通讯点的IP,而显示的是加密点的IP. 原文:https://www.cnblogs.com/yangyang1988/p/11559819.html

  7. SQL性能优化汇总

    SQL效率低下也是导致性能差的一个非常重要的原因,可以通过查看执行计划看SQL慢在哪里,一般情况,SQL效率低下原因主要有:   类别 子类 表达式或描述 原因 索引 未建索引 无 产生全表扫描 未利 ...

  8. 结合python版本安装python-devel gcc和g++的区别 安装前做yum搜索

    [test@ecs autocloudservices]# yum install python-develLoaded plugins: fastestmirrorLoading mirror sp ...

  9. Jenkins免密码登录

    Jenkins免密码登录 相关内容原文地址: 简书:海边的卡夫卡丶:Jenkins免密码登录 昨天惊现一个神奇的问题,Jenkins无法登录,无论是初始化的账号密码,还是admin用户,都登录不进去了 ...

  10. Java 实现Redis客户端,服务端

    Java 实现Redis客户端,服务端 1.Java实现Redis发布订阅 1.1实例 2.[Redis]Java实现redis消息订阅/发布(PubSub) 3.java实现 redis的发布订阅 ...