在前面的例子中,我们用互斥锁进行了明确的锁定来让共享的state 跨多个 Go 协程同步访问。另一个选择是使用内置的 Go协程和通道的的同步特性来达到同样的效果。这个基于通道的方法和 Go 通过通信以及 每个 Go 协程间通过通讯来共享内存,确保每块数据有单独的 Go 协程所有的思路是一致的。

Example:

package main
import (
"fmt"
"math/rand"
"sync/atomic"
"time"
)
//在这个例子中,state 将被一个单独的 Go 协程拥有。
// 这就能够保证数据在并行读取时不会混乱。
// 为了对 state 进行读取或者写入,其他的 Go 协程将发送一条数据到拥有的 Go协程中,
// 然后接收对应的回复。结构体 readOp 和 writeOp封装这些请求,并且是拥有 Go 协程响应的一个方式。 type readOp struct {
key int
resp chan int
}
type writeOp struct {
key int
val int
resp chan bool
}
func main() {
// 和前面一样,我们将计算我们执行操作的次数。 var ops int64
// reads 和 writes 通道分别将被其他 Go 协程用来发布读和写请求。 reads := make(chan *readOp)
writes := make(chan *writeOp)
// 这个就是拥有 state 的那个 Go 协程,和前面例子中的map一样,
// 不过这里是被这个状态协程私有的。这个 Go 协程反复响应到达的请求。
// 先响应到达的请求,然后返回一个值到响应通道 resp 来表示操作成功(或者是 reads 中请求的值) go func() {
var state = make(map[int]int)
for {
select {
case read := <-reads:
read.resp <- state[read.key]
case write := <-writes:
state[write.key] = write.val
write.resp <- true
}
}
}()
// 启动 100 个 Go 协程通过 reads 通道发起对 state 所有者Go 协程的读取请求。
// 每个读取请求需要构造一个 readOp,发送它到 reads 通道中,并通过给定的 resp 通道接收结果。 for r := 0; r < 100; r++ {
go func() {
for {
read := &readOp{
key: rand.Intn(5),
resp: make(chan int)}
reads <- read
<-read.resp
atomic.AddInt64(&ops, 1)
}
}()
}
// 用相同的方法启动 10 个写操作。 for w := 0; w < 10; w++ {
go func() {
for {
write := &writeOp{
key: rand.Intn(5),
val: rand.Intn(100),
resp: make(chan bool)}
writes <- write
<-write.resp
atomic.AddInt64(&ops, 1)
}
}()
}
// 让 Go 协程们跑 1s。 time.Sleep(time.Second)
// 最后,获取并报告 ops 值。 opsFinal := atomic.LoadInt64(&ops)
fmt.Println("ops:", opsFinal)
}

Result:

$ go run example.go
ops: 637568

运行这个程序显示这个基于 Go 协程的转台管理的例子达到了每秒大约 600,000 次操作

在这个特殊的例子中,基于 Go 协程的比基于互斥锁的稍复杂。这在某些例子中会有用,

例如,在你有其他通道包含其中或者当你管理多个这样的互斥锁容易出错的时候。你应该使用最自然的方法,特别是关于程序正确性的时候。

坐标: 上一个例子    下一个例子

go语言从例子开始之Example37.Go 状态协程的更多相关文章

  1. go语言学习--内核态和用户态(协程)

    go中的一个特点就是引入了相比于线程更加轻量级的协程(用户态的线程),那么什么是用户态和内核态呢? 一.什么是用户态和内核态 当一个任务(进程)执行系统调用而陷入内核代码中执行时,我们就称进程处于内核 ...

  2. Go语言 进程、线程、轻量级进程、协程和go中的Goroutine 那些事儿

    原文:http://www.cnblogs.com/shenguanpu/archive/2013/05/05/3060616.html 电话面试被问到go的协程,曾经的军伟也问到过我协程.虽然用py ...

  3. 038_go语言中的状态协程

    代码演示: package main import ( "fmt" "math/rand" "sync/atomic" "time ...

  4. 一个“蝇量级” C 语言协程库

    协程(coroutine)顾名思义就是“协作的例程”(co-operative routines).跟具有操作系统概念的线程不一样,协程是在用户空间利用程序语言的语法语义就能实现逻辑上类似多任务的编程 ...

  5. gRPC in ASP.NET Core 3.x -- Protocol Buffer(2)Go语言的例子(下)

    第一篇文章(大约半年前写的):https://www.cnblogs.com/cgzl/p/11246324.html gRPC in ASP.NET Core 3.x -- Protocol Buf ...

  6. go语言从例子开始之Example36.互斥锁

    在前面的例子中,我们看到了如何使用原子操作来管理简单的计数器.对于更加复杂的情况,我们可以使用一个互斥锁来在 Go 协程间安全的访问数据. Example: package main import ( ...

  7. go语言从例子开始之Example35.原子计数器

    Go 中最主要的状态管理方式是通过通道间的沟通来完成的,我们在工作池的例子中碰到过,但是还是有一些其他的方法来管理状态的.这里我们将看看如何使用 sync/atomic包在多个 Go 协程中进行 原子 ...

  8. go语言从例子开始之Example34.速率限制

    速率限制(英) 是一个重要的控制服务资源利用和质量的途径.Go 通过 Go 协程.通道和打点器优美的支持了速率限制. Example: package main import "fmt&qu ...

  9. go语言从例子开始之Example33.工作池

    在这个例子中,我们将看到如何使用 Go 协程和通道实现一个工作池 . Example: package main import "fmt" import "time&qu ...

随机推荐

  1. shelve模块 xml模块

    # import shelve# f=shelve.open('db.shl')# # f['stu1']={'name':'alex1','age':28}# # f['stu2']={'name' ...

  2. Codeforces 892E Envy

    问题描述 小Q正在玩一个叠塔的游戏,游戏的目标是叠出尽可能高的塔.在游戏中,一共有n张矩形卡片,其中第i张卡片的 长度为a_i,宽度为b_i.小Q需要把所有卡片按一定顺序叠成一座塔,要求对于任意一个矩 ...

  3. BZOJ 4407: 于神之怒加强版 莫比乌斯反演 + 线筛积性函数

    Description 给下N,M,K.求     Input 输入有多组数据,输入数据的第一行两个正整数T,K,代表有T组数据,K的意义如上所示,下面第二行到第T+1行,每行为两个正整数N,M,其意 ...

  4. UE4 中的Blutilities

    该功能是为编辑器中的简单扩展功能而设置的. 一般而言用蓝图在编辑器中做功能扩展都会用到Construction Script,但该功能有一些缺陷: 首先在actor发生任何变化(包括Transform ...

  5. 如何扩展 Create React App 的 Webpack 配置

    如何扩展 Create React App 的 Webpack 配置  原文地址https://zhaozhiming.github.io/blog/2018/01/08/create-react-a ...

  6. [CSS]CSS浮动塌陷及解决办法

    一. CSS浮动 先看一个例子 <html !DOCTYPE> <head> <title>HTML2</title> <style> .d ...

  7. drawRect

    1) 画笔设置 Paint.Style.STROKE 中空模式 paint = new Paint(); //新建一个画笔对象 paint.setAntiAlias(true);//抗锯齿功能 pai ...

  8. Android7.1后对于Toast类型窗口的限制

    在处理问题的时候,经常会遇到ANR,停止运行等问题,经过分析和验证,和如下的一行关键log有关 WindowManager: Adding more than one toast window for ...

  9. C-LeetCode-Shuffle an Array跑不过测试用例

    使用time()作为随机数种子,一秒改变一次随机数还是不够随机,跑不过测试用例6 ,在加上个毫秒级计时精度的clock()作为随机数种子就达标了. 示例代码: typedef struct { int ...

  10. Fedora 的截屏功能

    写写博客少不了截图,Windows 上使用微信的快捷键 Ctrl+A 截图并且可以随意编辑是挺方便的,开始在 Linux 上还没有找到这样的软件,只找到了不支持编辑的简单截图软件. 1. 使用 Scr ...