Goroutines和Channels(四)
如果说goroutine是Go语言程序的并发体的话,那么channels则是它们之间的通信机制。
一个channel是一个通信机制,它可以让一个goroutine通过它给另一个goroutine发送值信息。
每个channel都有一个特殊的类型,也就是channels可发送数据的类型。一个可以发送int类型数据的channel一般写为chan int。
使用内置的make函数,我们可以创建一个channel:
ch := make(chan int) // ch has type 'chan int'
和map类似,channel也对应一个make创建的底层数据结构的引用。
当我们复制一个channel或用于函数参数传递时,我们只是拷贝了一个channel引用,因此调用者和被调用者将引用同一个channel对象。
和其它的引用类型一样,channel的零值也是nil。
两个相同类型的channel可以使用==运算符比较。如果两个channel引用的是相同的对象,那么比较的结果为真。一个channel也可以和nil进行比较。
一个channel有发送和接受两个主要操作,都是通信行为。
一个发送语句将一个值从一个goroutine通过channel发送到另一个执行接收操作的goroutine。
发送和接收两个操作都使用<-运算符。
在发送语句中,<-运算符分割channel和要发送的值。
在接收语句中,<-运算符写在channel对象之前。一个不使用接收结果的接收操作也是合法的。
ch <- x // a send statement
x = <-ch // a receive expression in an assignment statement
<-ch // a receive statement; result is discarded
Channel还支持close操作,用于关闭channel,随后对基于该channel的任何发送操作都将导致panic异常。
对一个已经被close过的channel进行接收操作依然可以接受到之前已经成功发送的数据;如果channel中已经没有数据的话将产生一个零值的数据。
使用内置的close函数就可以关闭一个channel:
close(ch)
以最简单方式调用make函数创建的是一个无缓存的channel。
但是我们也可以指定第二个整型参数,对应channel的容量。如果channel的容量大于零,那么该channel就是带缓存的channel。
ch = make(chan int) // unbuffered channel
ch = make(chan int, 0) // unbuffered channel
ch = make(chan int, 3) // buffered channel with capacity 3
一个基于无缓存Channels的发送操作将导致发送者goroutine阻塞,直到另一个goroutine在相同的Channels上执行接收操作,当发送的值通过Channels成功传输之后,两个goroutine可以继续执行后面的语句。反之,如果接收操作先发生,那么接收者goroutine也将阻塞,直到有另一个goroutine在相同的Channels上执行发送操作。
基于无缓存Channels的发送和接收操作将导致两个goroutine做一次同步操作。因为这个原因,无缓存Channels有时候也被称为同步Channels。当通过一个无缓存Channels发送数据时,接收者收到数据发生在唤醒发送者goroutine之前(译注:happens before,这是Go语言并发内存模型的一个关键术语!)。
在讨论并发编程时,当我们说x事件在y事件之前发生(happens before),我们并不是说x事件在时间上比y时间更早;我们要表达的意思是要保证在此之前的事件都已经完成了,例如在此之前的更新某些变量的操作已经完成,你可以放心依赖这些已完成的事件了。
当我们说x事件既不是在y事件之前发生也不是在y事件之后发生,我们就说x事件和y事件是并发的。这并不是意味着x事件和y事件就一定是同时发生的,我们只是不能确定这两个事件发生的先后顺序。在下一章中我们将看到,当两个goroutine并发访问了相同的变量时,我们有必要保证某些事件的执行顺序,以避免出现某些并发问题。(意思就是不确定线程执行顺序)
func main() {
conn, err := net.Dial("tcp", "localhost:8000")
if err != nil {
log.Fatal(err)
}
done := make(chan struct{})
go func() {
io.Copy(os.Stdout, conn) // NOTE: ignoring errors
log.Println("done")
done <- struct{}{} // signal the main goroutine
}()
mustCopy(conn, os.Stdin)
conn.Close()
<-done // wait for background goroutine to finish
}
当用户关闭了标准输入,主goroutine中的mustCopy函数调用将返回,然后调用conn.Close()关闭读和写方向的网络连接。
关闭网络连接中的写方向的连接将导致server程序收到一个文件(end-of-file)结束的信号。
关闭网络连接中读方向的连接将导致后台goroutine的io.Copy函数调用返回一个“read from closed connection”(“从关闭的连接读”)类似的错误,因此我们临时移除了错误日志语句;(需要注意的是go语句调用了一个函数字面量,这Go语言中启动goroutine常用的形式。)
在后台goroutine返回之前,它先打印一个日志信息,然后向done对应的channel发送一个值。主goroutine在退出前先等待从done对应的channel接收一个值。因此,总是可以在程序退出前正确输出“done”消息。
基于channels发送消息有两个重要方面。首先每个消息都有一个值,但是有时候通讯的事实和发生的时刻也同样重要。当我们更希望强调通讯发生的时刻时,我们将它称为消息事件。有些消息事件并不携带额外的信息,它仅仅是用作两个goroutine之间的同步,这时候我们可以用struct{}空结构体作为channels元素的类型,虽然也可以使用bool或int类型实现同样的功能,done <- 1语句也比done <- struct{}{}更短。
Goroutines和Channels(四)的更多相关文章
- 如果这种方式导致程序明显变慢或者引起其他问题,我们要重新思考来通过 goroutines 和 channels 来解决问题
https://github.com/Unknwon/the-way-to-go_ZH_CN/blob/master/eBook/09.3.md 9.3 锁和 sync 包 在一些复杂的程序中,通常通 ...
- Goroutines和Channels
原文链接 https://golangbot.com/goroutines/ Goroutines Goroutines 可以被认为是多个函数或方法同时允许.可以认为是一个轻量级的线程.与线程的花费相 ...
- Goroutines和Channels(五)
Channels也可以用于将多个goroutine连接在一起,一个Channel的输出作为下一个Channel的输入.这种串联的Channels就是所谓的管道(pipeline).下面的程序用两个ch ...
- Goroutines和Channels(三)
clock服务器每一个连接都会起一个goroutine.在本节中我们会创建一个echo服务器,这个服务在每个连接中会有多个goroutine.大多数echo服务仅仅会返回他们读取到的内容,就像下面这个 ...
- Goroutines和Channels(二)
网络编程是并发大显身手的一个领域,由于服务器是最典型的需要同时处理很多连接的程序,这些连接一般来自于彼此独立的客户端. 本小节,我们会讲解go语言的net包,这个包提供编写一个网络客户端或者服务器程序 ...
- Goroutines和Channels(一)
Go语言中的并发程序可以用两种手段来实现.本章讲解goroutine和channel,其支持“顺序通信进程”(communicating sequential processes)或被简称为CSP.C ...
- ARTS-S golang goroutines and channels(二)
向tcp服务端发消息 package main import ( "io" "log" "net" "os" ) fun ...
- ARTS-S golang goroutines and channels(一)
先用golang实现一个简单的tcp服务端,假定文件名为clock1.go // clock1.go package main import ( "fmt" "io&qu ...
- [PHP] 2018年终总结
去掉敏感信息后的不完整版 ==========================================================================2018年12月29日 记 ...
随机推荐
- POJ2485:Highways(模板题)
http://poj.org/problem?id=2485 Description The island nation of Flatopia is perfectly flat. Unfortun ...
- ios 工作日志
1.设计模式 1.1 想用一个controllerK控制多个页面的切换 但是每一个页面必须要引用这个controller,这样才能控制进度, 所以必须是弱引用.controller必须被某一个实例强引 ...
- git 下载单个文件 已经添加多个远程服务器
175down vote It is possible to do (in the deployed repository) git fetch followed by git checkout or ...
- nodejs 将网上的图片下载到本地文件
var request = require('request'); var fs = require('fs'); var img_src = 'https://www.baidu.com/img/b ...
- 7.7 Models -- Working with Records
Modifying Attributes 1. 一旦一条record被加载,你可以开始改变它的属性.在Ember.js对象中属性的行为就像正常的属性.作出改变就像设置你想要改变的属性一样简单: var ...
- mysql_connect
in this passage, we slove the problem about Mysql_connect. first, let' see an example: resource mysq ...
- VS2010/MFC编程入门之三十五(菜单:菜单及CMenu类的使用)
鸡啄米在上一节中讲的是VS2010的菜单资源,本节主要讲菜单及CMenu类的使用. CMenu类的主要成员函数 MFC为菜单的操作提供了CMenu类,下面鸡啄米就常用的几个成员函数进行简单的介绍. B ...
- Java(1-15)
1.方法参数是引用类型,传递的是内存地址!2.方法的重载特性:在同一个类中,允许方法同名,只要方法的参数列表不同即可!3.Stirng特殊在如果静态数据区中存在,那么不创建新的对象,而是指向这个对象. ...
- Python Web学习笔记之SSL,TLS,HTTPS
一. SSL 1. SSL简介 SSL协议位于TCP/IP协议与各种应用层协议之间,为数据通讯提供安全支持.SSL协议可分为两层: SSL记录协议(SSL Record Protocol):它建立在可 ...
- bzoj1648 / P2853 [USACO06DEC]牛的野餐Cow Picnic
P2853 [USACO06DEC]牛的野餐Cow Picnic 你愿意的话,可以写dj. 然鹅,对一个缺时间的退役选手来说,暴力模拟是一个不错的选择. 让每个奶牛都把图走一遍,显然那些被每个奶牛都走 ...