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日 记 ...
随机推荐
- PAT 1032 Sharing[hash][链表][一般上]
1032 Sharing (25)(25 分) To store English words, one method is to use linked lists and store a word l ...
- soapUI-Conditional Goto
1.1.1 Conditional Goto 1.1.1.1 概述 - Conditional Goto Conditional Goto TestStep包含任意数量的XPath/JSONPath ...
- [LeetCode] 103. Binary Tree Zigzag Level Order Traversal _ Medium tag: BFS
Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to ...
- [LeetCode] questions conclustion_Path in Tree
Path in Tree: [LeetCode] 112. Path Sum_Easy tag: DFS input: root, target, return True if exi ...
- javascript console自动点击页面元素
原文地址https://jingyan.baidu.com/article/0a52e3f41c0b0abf63ed726d.html 这里拿百度音乐VIP兑换页面来做演示. 首先打开百度音乐VIP兑 ...
- python repr方法和str方法
每个类都有默认的__repr__, __str__方法,用print 实例时调用类的str方法,直接输出类的实例,调用的是类的repr方法 在命令行界面,不用print命令打印而是直接写变量名,就是用 ...
- 彻底明白Flink系统学习5:window、Linux本地安装Flink
http://www.aboutyun.com/thread-26393-1-1.html 问题导读 1.如何在window下安装Flink? 2.Flink本地安装启动命令与原先版本有什么区别? 3 ...
- 小白也能看懂的插件化DroidPlugin原理(一)-- 动态代理
前言:插件化在Android开发中的优点不言而喻,也有很多文章介绍插件化的优势,所以在此不再赘述.前一阵子在项目中用到 DroidPlugin 插件框架 ,近期准备投入生产环境时出现了一些小问题,所以 ...
- 干货:Java并发编程系列之synchronized(一)
1. 使用方法 synchronized 是 java 中最常用的保证线程安全的方式,synchronized 的作用主要有三方面: 确保线程互斥的访问代码块,同一时刻只有一个方法可以进入到临界区 保 ...
- Python Web学习笔记之TCP/IP协议原理与介绍
HTTP.FTP.SMTP.Telnet等等协议,哦!那个HTTP协议啊就是访问网页用的那个协议啊然后那个······其实······你懂得,我们应该从实际来了解他,理解网络协议的作用与功能,然后再从 ...