初接触 go 有不对得请解释下

Channel是goroutine之间进行通信的一种方式,先看下面代码为什么会阻塞:

func closer(ch chan int) {
ch <- 1
log.Println(1) }
func main() {
ch := make(chan int)
closer(ch)
out := <-ch
log.Println(out)
}

主要用于同步,在不同goruntine中,在一个goruntine中就会永远阻塞.

放在不同goruntine中就会达到同步效果, 或者增加buffer

func closer2(ch chan int) {
go func() {
ch <- 1
log.Println(1)
}() }
func main() {
ch := make(chan int)
closer2(ch)
out := <-ch
log.Println(out)
}
func main() {
ch := make(chan int,1)
ch <- 1
<-ch
}

---------------------------------------------------------------------------------

下面是一篇对channel比较详细介绍

from http://www.cnblogs.com/hustcat/p/4003729.html

Channel

1. 概述

“网络,并发”是Go语言的两大feature。Go语言号称“互联网的C语言”,与使用传统的C语言相比,写一个Server所使用的代码更少,也更简单。写一个Server除了网络,另外就是并发,相对python等其它语言,Go对并发支持使得它有更好的性能。

Goroutine和channel是Go在“并发”方面两个核心feature。

Channel是goroutine之间进行通信的一种方式,它与Unix中的管道类似。

Channel声明:

ChannelType = ( "chan" | "chan" "<-" | "<-" "chan" ) ElementType .

例如:

var ch chan int

var ch1 chan<- int  //ch1只能写

var ch2 <-chan int  //ch2只能读

channel是类型相关的,也就是一个channel只能传递一种类型。例如,上面的ch只能传递int。

在go语言中,有4种引用类型:slice,map,channel,interface。

Slice,map,channel一般都通过make进行初始化:

ci := make(chan int)            // unbuffered channel of integers

cj := make(chan int, 0)         // unbuffered channel of integers

cs := make(chan *os.File, 100)  // buffered channel of pointers to Files

创建channel时可以提供一个可选的整型参数,用于设置该channel的缓冲区大小。该值缺省为0,用来构建默认的“无缓冲channel”,也称为“同步channel”。

Channel作为goroutine间的一种通信机制,与操作系统的其它通信机制类似,一般有两个目的:同步,或者传递消息。

2. 同步

c := make(chan int)  // Allocate a channel.

// Start the sort in a goroutine; when it completes, signal on the channel.

go func() {

list.Sort()

c <- 1  // Send a signal; value does not matter.

}()

doSomethingForAWhile()

<-c   // Wait for sort to finish; discard sent value.

上面的示例中,在子goroutine中进行排序操作,主goroutine可以做一些别的事情,然后等待子goroutine完成排序。

接收方会一直阻塞直到有数据到来。如果channel是无缓冲的,发送方会一直阻塞直到接收方将数据取出。如果channel带有缓冲区,发送方会一直阻塞直到数据被拷贝到缓冲区;如果缓冲区已满,则发送方只能在接收方取走数据后才能从阻塞状态恢复。

3. 消息传递

我们来模拟一下经典的生产者-消费者模型。

func Producer (queue chan<- int){

for i:= 0; i < 10; i++ {

queue <- i

}

}

func Consumer( queue <-chan int){

for i :=0; i < 10; i++{

v := <- queue

fmt.Println("receive:", v)

}

}

func main(){

queue := make(chan int, 1)

go Producer(queue)

go Consumer(queue)

time.Sleep(1e9) //让Producer与Consumer完成

}

上面的示例在Producer中生成数据,在Consumer中处理数据。

4. Server编程模型

在server编程,一种常用的模型:主线程接收请求,然后将请求分发给工作线程,工作线程完成请求处理。用go来实现,如下:

func handle(r *Request) {

process(r)  // May take a long time.

}

func Serve(queue chan *Request) {

for {

req := <-queue

go handle(req)  // Don't wait for handle to finish.

}

}

一般来说,server的处理能力不是无限的,所以,有必要限制线程(或者goroutine)的数量。在C/C++编程中,我们一般通过信号量来实现,在go中,我们可以通过channel达到同样的效果:

var sem = make(chan int, MaxOutstanding)

func handle(r *Request) {

sem <- 1    // Wait for active queue to drain.

process(r)  // May take a long time.

<-sem       // Done; enable next request to run.

}

func Serve(queue chan *Request) {

for {

req := <-queue

go handle(req)  // Don't wait for handle to finish.

}

}

我们通过引入sem channel,限制了同时最多只有MaxOutstanding个goroutine运行。但是,上面的做法,只是限制了运行的goroutine的数量,并没有限制goroutine的生成数量。如果请求到来的速度过快,会导致产生大量的goroutine,这会导致系统资源消耗完全。

为此,我们有必要限制goroutine的创建数量:

func Serve(queue chan *Request) {

for req := range queue {

sem <- 1

go func() {

process(req) // Buggy; see explanation below.

<-sem

}()

}

}

上面的代码看似简单清晰,但在go中,却有一个问题。Go语言中的循环变量每次迭代中是重用的,更直接的说就是req在所有的子goroutine中是共享的,从变量的作用域角度来说,变量req对于所有的goroutine,是全局的。

这个问题属于语言实现的范畴,在C语言中,你不应该将一个局部变量传递给另外一个线程去处理。有很多解决方法,这里有一个讨论。从个人角度来说,我更倾向下面这种方式:

func Serve(queue chan *Request) {

for req := range queue {

sem <- 1

go func(r *Request) {

process(r)

<-sem

}(req)

}

}

至少,这样的代码不会让一个go的初学者不会迷糊,另外,从变量的作用域角度,也更符合常理一些。

在实际的C/C++编程中,我们倾向于工作线程在一开始就创建好,而且线程的数量也是固定的。在go中,我们也可以这样做:

func handle(queue chan *Request) {

for r := range queue {

process(r)

}

}

func Serve(clientRequests chan *Request, quit chan bool) {

// Start handlers

for i := 0; i < MaxOutstanding; i++ {

go handle(clientRequests)

}

<-quit  // Wait to be told to exit.

}

开始就启动固定数量的handle goroutine,每个goroutine都直接从channel中读取请求。这种写法比较简单,但是不知道有没有“惊群”问题?有待后续分析goroutine的实现。

5. 传递channel的channel

channel作为go语言的一种原生类型,自然可以通过channel进行传递。通过channel传递channel,可以非常简单优美的解决一些实际中的问题。

在上一节中,我们主goroutine通过channel将请求传递给工作goroutine。同样,我们也可以通过channel将处理结果返回给主goroutine。

主goroutine:

type Request struct {

args        []int

resultChan  chan int

}

request := &Request{[]int{3, 4, 5}, make(chan int)}

// Send request

clientRequests <- request

// Wait for response.

fmt.Printf("answer: %d\n", <-request.resultChan)

主goroutine将请求发给request channel,然后等待result channel。子goroutine完成处理后,将结果写到result channel。

func handle(queue chan *Request) {

for req := range queue {

result := do_something()

req.resultChan <- result

}

}

6. 多个channel

在实际编程中,经常会遇到在一个goroutine中处理多个channel的情况。我们不可能阻塞在两个channel,这时就该select场了。与C语言中的select可以监控多个fd一样,go语言中select可以等待多个channel。

c1 := make(chan string)

c2 := make(chan string)

go func() {

time.Sleep(time.Second * 1)

c1 <- "one"

}()

go func() {

time.Sleep(time.Second * 2)

c2 <- "two"

}()

for i := 0; i < 2; i++ {

select {

case msg1 := <-c1:

fmt.Println("received", msg1)

case msg2 := <-c2:

fmt.Println("received", msg2)

}

}

在C中,我们一般都会传一个超时时间给select函数,go语言中的select没有该参数,相当于超时时间为0。

主要参考

https://golang.org/doc/effective_go.html

go channel 阻塞的更多相关文章

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

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

  2. go语言中sync包和channel机制

    文章转载至:https://www.bytelang.com/article/content/A4jMIFmobcA= golang中实现并发非常简单,只需在需要并发的函数前面添加关键字"Go&quo ...

  3. Java NIO 之 Socket Channel

    在Java NIO中用Channel来对程序与进行I/O操作主体的连接关系进行抽象,这些IO主体包括如文件.Socket或其他设备.简而言之,指代了一种与IO操作对象间的连接关系. 按照Channel ...

  4. Golang学习笔记:channel

    channel channel是goroutine之间的通信机制,它可以让一个goroutine通过它给另一个goroutine发送数据,每个channel在创建的时候必须指定一个类型,指定的类型是任 ...

  5. Go语言系列(八)- Goroute和Channel

    一.Goroute 1. 进程和线程 A. 进程是程序在操作系统中的一次执行过程,系统进行资源分配合调度的一个独立单位 B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能 ...

  6. [日常] Go语言圣经-并发的非阻塞缓存

    1.go test命令是一个按照约定和组织进行测试的程序2.竞争检查器 go run -race 附带一个运行期对共享变量访问工具的test,出现WARNING: DATA RACE 说明有数据竞争3 ...

  7. [转帖]go 的goroutine 以及 channel 的简介.

    进程,线程的概念在操作系统的书上已经有详细的介绍.进程是内存资源管理和cpu调度的执行单元.为了有效利用多核处理器的优势,将进程进一步细分,允许一个进程里存在多个线程,这多个线程还是共享同一片内存空间 ...

  8. golang中并发sync和channel

    golang中实现并发非常简单,只需在需要并发的函数前面添加关键字"go",但是如何处理go并发机制中不同goroutine之间的同步与通信,golang 中提供了sync包和channel ...

  9. 一个小事例,了解golang通道阻塞模式

    在学习golang中,channel真的是让人捉摸不透的东西,本来我自以为我理解了协程阻塞的用法了,结果就下面这个小例子,我还是在打印输出后才搞明白到底怎么回事? 当然了,这也是我自身对协程这块不太熟 ...

随机推荐

  1. ios 如何对UITableView中的内容进行排序

    - (UITableViewCellEditingStyle)tableView:(UITableView *)tableView editingStyleForRowAtIndexPath:(NSI ...

  2. ORACLE WITH AS 用法,创建临时表

    语法: with tempName as (select ....) select ... –针对一个别名with tmp as (select * from tb_name) –针对多个别名with ...

  3. 170330、Spring中你不知道的注入方式

    前言 在Spring配置文件中使用XML文件进行配置,实际上是让Spring执行了相应的代码,例如: 使用<bean>元素,实际上是让Spring执行无参或有参构造器 使用<prop ...

  4. 170310、Jenkins部署Maven多环境项目(dev、beta、prod)的参数设置

    使用Jenkins配置Git+Maven的自动化构建: http://blog.csdn.net/xlgen157387/article/details/50353317 在一个多开发和生产环境的项目 ...

  5. 160311、mybatis sql语句中转义字符

    问题: 在mapper  ***.xml中的sql语句中,不能直接用大于号.小于号要用转义字符 解决方法:   1.转义字符串 小于号    <    < 大于号    >    & ...

  6. vscode编辑器配置C语言编译运行环境

    1.安装C/C++插件 2.安装编译环境,这里选择MinGW(http://mingw.org/ ) 选择一个安装目录,如:E:\workspace\MinGW mingw32-gcc开头的(包括了m ...

  7. flask系列

    1.flask基础 2.flask上下文 3.flask源码剖析--请求流程 4.数据库连接池DButils 5.Flask-Session 6.WTForms 7.Flask-SQLAlchemy ...

  8. leadecode 2 Add Two Numbers

    package leadcode; /** * 2. Add Two Numbers * Medium * 4067 * 953 * * * You are given two non-empty l ...

  9. URL地址中的字符串转换

    url出现了有+,空格,/,?,%,#,&,=等特殊符号的时候,可能在服务器端无法获得正确的参数值,怎么解决?解决办法将这些字符转化成服务器可以识别的字符,对应关系如下:URL字符转义 用其它 ...

  10. Spark Standalone Mode 多机启动 -- 分布式计算系统spark学习(二)(更新一键启动slavers)

    捣鼓了一下,先来个手动挡吧.自动挡要设置ssh无密码登陆啥的,后面开搞. 一.手动多台机链接master 手动链接master其实上篇已经用过. 这里有两台机器: 10.60.215.41 启动mas ...