文档章节

带buffer的chan能同步吗?

chai2010
 chai2010
发布于 2014/05/08 11:43
字数 432
阅读 201
收藏 0

具体看下面2个例子:

无 buffer 的: http://play.golang.org/p/RwPbCcueWh

func main() {
	ch := make(chan bool)
	go func() {
		println("fuck buffer channel")
		<-ch
	}()
	ch <- true
}

有buffer的: http://play.golang.org/p/YV9H6WPYuJ

func main() {
	ch := make(chan bool, 1)
	go func() {
		println("fuck buffer channel")
		<-ch
	}()
	ch <- true
}

第二个程序(有buffer的chan)无法保证收/发同步, 导致main退出时 println 还未执行. 说明带buffer的chan是不能保证收/发同步.

下面是一个构造的测试例子 http://play.golang.org/p/vQQCv9nG15

package main

import (
	"fmt"
	"sync"
	"time"
)

func main() {
	testNoBufferChan()
	testBufferChan()
}

func testNoBufferChan() {
	wg := new(sync.WaitGroup)
	ch := make(chan bool)

	wg.Add(1)
	go func() {
		defer wg.Done()

		time.Sleep(10 * time.Second)

		fmt.Println(time.Now(), "NoBufferChan recv begin")
		<-ch
		fmt.Println(time.Now(), "NoBufferChan recv end")
	}()

	fmt.Println(time.Now(), "NoBufferChan send begin")
	ch <- true
	fmt.Println(time.Now(), "NoBufferChan send end")

	wg.Wait()
}

func testBufferChan() {
	wg := new(sync.WaitGroup)
	ch := make(chan bool, 1)

	wg.Add(1)
	go func() {
		defer wg.Done()

		time.Sleep(10 * time.Second)

		fmt.Println(time.Now(), "BufferChan recv begin")
		<-ch
		fmt.Println(time.Now(), "BufferChan recv end")
	}()

	fmt.Println(time.Now(), "BufferChan send begin")
	ch <- true
	fmt.Println(time.Now(), "BufferChan send end")

	wg.Wait()
}

在 play 上的结果(play上的是假时间):

2009-11-10 23:00:00 +0000 UTC NoBufferChan send begin
2009-11-10 23:00:10 +0000 UTC NoBufferChan recv begin
2009-11-10 23:00:10 +0000 UTC NoBufferChan recv end
2009-11-10 23:00:10 +0000 UTC NoBufferChan send end
2009-11-10 23:00:10 +0000 UTC BufferChan send begin
2009-11-10 23:00:10 +0000 UTC BufferChan send end
2009-11-10 23:00:20 +0000 UTC BufferChan recv begin
2009-11-10 23:00:20 +0000 UTC BufferChan recv end

在本机运行的结果:

2014-05-09 10:41:17.5611304 +0800 CST NoBufferChan send begin
2014-05-09 10:41:27.5637025 +0800 CST NoBufferChan recv begin
2014-05-09 10:41:27.5637025 +0800 CST NoBufferChan recv end
2014-05-09 10:41:27.5637025 +0800 CST NoBufferChan send end
2014-05-09 10:41:27.5647026 +0800 CST BufferChan send begin
2014-05-09 10:41:27.5647026 +0800 CST BufferChan send end
2014-05-09 10:41:37.5662746 +0800 CST BufferChan recv begin
2014-05-09 10:41:37.5662746 +0800 CST BufferChan recv end

可以发现带Buffer的chan收/发完成时间相差了10秒.

© 著作权归作者所有

chai2010

chai2010

粉丝 428
博文 102
码字总数 81536
作品 10
武汉
程序员
私信 提问
加载中

评论(3)

♂茶舞
♂茶舞
sync.WaitGroup 这个只是保证线程能执行完,与 chan 没有关系;
第二个程序,只是写chan的时候,恰好能写进去,就执行完了,线程没有执行完就结束了;
另外 chan 的 本质 与 capicity 大小没有直接关系,本质是堵塞与不堵塞,只要不堵塞,主线程执行完,就结束;
chai2010
chai2010 博主
刚才 向 @星星@喻恒春 请教了关于 unbuffer chan 和 buffer chan 的同步问题.

发现之前对 buffer chan 不能同步的认识有错误.

本质上讲 unbuffer chan 和 buffer chan 是一样的,
只是 make(chan, 0), make(chan, 1), make(chan, 2) ... 参数的差别.

特别是: if cap(chan) - len(chan) == 0 then block

所谓 block, 就是 send 和 recv 都会阻塞.

send 和 recv 发生的时间顺序受这3个规则约束:

- A send on a channel happens before the corresponding receive from that channel completes.
- A receive from an unbuffered channel happens before the send on that channel completes.
- The kth send on a channel with capacity C happens before the k+Cth receive from that channel completes.

这里主要是一个 happens before 的概念.

我的理解是 channel completes 是一个原子操作, 分别对应 send 和 recv.

也就是说 unbuffer chan 的 send 和 recv 在 channel completes 后 数据就出了 chan 了.
对于 buffer(C) chan , 第 k 个 send 和 第 k+C recv 也是在 channel completes 后 数据就出了 chan 了.

我之前写的2个测试程序并不能说明什么问题. 因为他们对应的是chan的不同状态(cap(chan) - len(chan) == 0 ?).
不同的状态有不同的行为, 但是都是合理的.

结论: unbuffer chan 和 buffer chan 一个东西(只是buffer大小的差别).
chai2010
chai2010 博主
该文观点有误, 待整理.
Golang Channel用法简编

在进入正式内容前,我这里先顺便转发一则消息,那就是Golang 1.3.2已经正式发布了。国内的golangtc已经镜像了golang.org的安装包下载页面,国内go程序员与爱好者们可以到"Golang中 国",即g...

nop4ss
2015/07/23
200
0
golang channel 用法

golang channel 用法转的 一、Golang并发基础理论 Golang在并发设计方面参考了C.A.R Hoare的CSP,即Communicating Sequential Processes并发模型理论。但就像John Graham-Cumming所说的那样,...

wangxuwei
2018/01/29
221
0
全志H3_dma接口使用说明书

2. Dmaengine 框架 2.1.基本概述 Dmaengine 是 linux 内核 dma 驱动框架,针对 DMA 驱动的混乱局面内核社区提出了一个全新的框架驱动,目标在统一 dma API 让各个模块使用 DMA 时不用关心硬件...

牛牛00
05/28
0
0
Go语言_并发篇

当被问到为什么用Go语言,一定不得不提的是Go语言的并发程序编写。在C语言中编写非常繁琐复杂的并发程序在Go语言中总是显得如此便捷。 Go中并发程序依靠的是两个:goroutine和channel 理解什...

王二狗子11
2018/01/08
0
0
Go语言_并发篇

当被问到为什么用Go语言,一定不得不提的是Go语言的并发程序编写。在C语言中编写非常繁琐复杂的并发程序在Go语言中总是显得如此便捷。 Go中并发程序依靠的是两个:goroutine和channel 理解什...

晨曦之光
2012/06/07
1K
1

没有更多内容

加载失败,请刷新页面

加载更多

刚哥谈架构 (二) 我眼中的架构师

之前在公司,有小伙伴在向别人介绍我的时候,经常会有人这么说:“刚哥是我们的architcture”,如果来人是老外,心中一定是一惊,心中暗叹,“这位匪首看上去貌不惊人,难道已经做到了架构和...

naughty
52分钟前
5
0
OSChina 周日乱弹 —— 别问,问就是没空

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @tom_tdhzz :#今日歌曲推荐# 分享容祖儿/彭羚的单曲《心淡》: 《心淡》- 容祖儿/彭羚 手机党少年们想听歌,请使劲儿戳(这里) @wqp0010 :周...

小小编辑
今天
131
4
golang微服务框架go-micro 入门笔记2.1 micro工具之micro api

micro api micro 功能非常强大,本文将详细阐述micro api 命令行的功能 重要的事情说3次 本文全部代码https://idea.techidea8.com/open/idea.shtml?id=6 本文全部代码https://idea.techidea8....

非正式解决方案
今天
5
0
Spring Context 你真的懂了吗

今天介绍一下大家常见的一个单词 context 应该怎么去理解,正确的理解它有助于我们学习 spring 以及计算机系统中的其他知识。 1. context 是什么 我们经常在编程中见到 context 这个单词,当...

Java知其所以然
昨天
5
0
Spring Boot + Mybatis-Plus 集成与使用(二)

前言: 本章节介绍MyBatis-Puls的CRUD使用。在开始之前,先简单讲解下上章节关于Spring Boot是如何自动配置MyBatis-Plus。 一、自动配置 当Spring Boot应用从主方法main()启动后,首先加载S...

伴学编程
昨天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部