go routine channel select

2023-05-16

一、go routine channel

package main

import (
	"fmt"
	"time"
)

func worker(id int, c chan int) {
	for n := range c {//读取channel
		fmt.Printf("Worker %d received %c\n",
			id, n)
	}
}

func createWorker(id int) chan<- int {
	c := make(chan int)
	go worker(id, c)//routine,可以理解为新开线程
	return c
}

func chanDemo() {
	var channels [10]chan<- int
	for i := 0; i < 10; i++ {
		channels[i] = createWorker(i)
	}

	for i := 0; i < 10; i++ {
		channels[i] <- 'a' + i//向channel中写入数据
	}

	for i := 0; i < 10; i++ {
		channels[i] <- 'A' + i
	}

	time.Sleep(time.Second)//主线程也是个runtine
}

func bufferedChannel() {
	c := make(chan int, 3)
	go worker(0, c)
	c <- 'a'
	c <- 'b'
	c <- 'c'//如果没有人接收,可以在缓冲区存放3个,发送第四个时候会阻塞;如果make(chan int),如果没有人接收,在发送第二个时候,就会阻塞。
	c <- 'd'
	time.Sleep(time.Millisecond)
}

func channelClose() {
	c := make(chan int)
	go worker(0, c)
	c <- 'a'
	c <- 'b'
	c <- 'c'
	c <- 'd'
	close(c)//关闭channel后不能发送和接收数据了
	time.Sleep(time.Second * 20)
}

func main() {
	fmt.Println("Channel as first-class citizen")
	chanDemo()
	//fmt.Println("Buffered channel")
	//bufferedChannel()
	//fmt.Println("Channel close and range")
	//channelClose()
}

输出:

Worker 0 received a
Worker 1 received b
Worker 2 received c
Worker 3 received d
Worker 4 received e
Worker 4 received E
Worker 0 received A
Worker 7 received h
Worker 2 received C
Worker 9 received j
Worker 1 received B
Worker 6 received g
Worker 3 received D
Worker 8 received i
Worker 5 received f
Worker 5 received F
Worker 8 received I
Worker 6 received G
Worker 7 received H
Worker 9 received J
go runtine可以理解为新开个线程,不过实际上很多runtime可以运行在一个线程中,由go虚拟机去调度。

go channel是为了不同go runtine间通信而设置的。

1、发送channel后,只能在其他runtine中接收到channel后,才会继续执行下条语句,否则在发送channel时睡眠等待

2、如果是接收channel,如果没有发送者,则会一直等待,直到发送channel。

package main

import (
	"fmt"
	"time"
)

func worker(id int, c chan int) {
	time.Sleep(time.Second * 5)
	for n := range c {
		fmt.Printf("Worker %d received %c\n",
			id, n)
	}
}

func createWorker(id int) chan<- int {
	c := make(chan int)
	go worker(id, c)
	return c
}


func channelClose() {
	c := make(chan int)
	go worker(0, c)
	c <- 'a'//发送channel后,只能在其他runtine中接收到channel后,才会继续执行下条语句,否则在发送channel时睡眠等待
	fmt.Println("run here")
	c <- 'b'
	c <- 'c'
	c <- 'd'
	time.Sleep(time.Second * 20)
}

func main() {
	fmt.Println("Channel close and range")
	channelClose()
}

先睡眠等待5秒后,才打印run here。

 

二、channel完整例子

package main

import (
	"fmt"
)

func doWork(id int,
	w worker) {
	for n := range w.in {
		fmt.Printf("Worker %d received %c\n",
			id, n)
		w.done <- true
	}
}

type worker struct {
	in   chan int
	done chan bool
}

func createWorker(
	id int) worker {
	w := worker{
		in: make(chan int),
		done: make(chan bool),
	}
	go doWork(id, w)
	return w
}

func chanDemo() {

	var workers [10]worker
	for i := 0; i < 10; i++ {
		workers[i] = createWorker(i)
	}


	for i, worker := range workers {
		worker.in <- 'a' + i
	}

	for _, worker := range workers {
		<-worker.done
	}

	for i, worker := range workers {
		worker.in <- 'A' + i
	}

	for _, worker := range workers {
		<-worker.done
	}
}

func main() {
	chanDemo()
}

输出:

Worker 9 received j
Worker 8 received i
Worker 7 received h
Worker 0 received a
Worker 2 received c
Worker 1 received b
Worker 3 received d
Worker 5 received f
Worker 6 received g
Worker 4 received e
Worker 1 received B
Worker 0 received A
Worker 3 received D
Worker 2 received C
Worker 4 received E
Worker 5 received F
Worker 9 received J
Worker 8 received I
Worker 6 received G
Worker 7 received H

package main

import (
	"fmt"
)

func doWork(id int,
	w worker) {
	for n := range w.in {
		fmt.Printf("Worker %d received %c\n",
			id, n)
		w.done <- true//没有人接收,会卡在这句上,而马上又要发送channel,接收的语句还在发送channel后面,死锁
	}
}

type worker struct {
	in   chan int
	done chan bool
}

func createWorker(
	id int) worker {
	w := worker{
		in: make(chan int),
		done: make(chan bool),
	}
	go doWork(id, w)
	return w
}

func chanDemo() {

	var workers [10]worker
	for i := 0; i < 10; i++ {
		workers[i] = createWorker(i)
	}


	for i, worker := range workers {
		worker.in <- 'a' + i
	}

	for i, worker := range workers {
		worker.in <- 'A' + i
	}

	for _, worker := range workers {
		<-worker.done
		<-worker.done
	}
}

func main() {
	chanDemo()
}

输出:

Worker 8 received i
Worker 9 received j
Worker 1 received b
Worker 3 received d
Worker 5 received f

Worker 4 received e
Worker 6 received g
Worker 2 received c
Worker 0 received a
fatal error: all goroutines are asleep - deadlock!
 

三、select

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func generator() chan int {
	out := make(chan int)
	go func() {
		i := 0
		for {
			time.Sleep(time.Duration(rand.Intn(1500))* time.Millisecond)
			out <- i
			i++
		}
	}()
	return out
}

func main() {
	var c1 ,c2 = generator(), generator()
	var c3 chan int
	c3 = make(chan int)
	for {
		select {
		case n := <-c1:
			fmt.Println("Received from c1:", n)
		case n := <-c2:
			fmt.Println("Received from c2:", n)
		case c3 <- 1:
			fmt.Println("send c3:")
		}
	}


}

select和不加select有什么区别呢?

不加select:

如果接收时,没有人发送,会睡眠等待,如n := <-c1。如果发送,没有人接收,会睡眠等待如c3 <- 1。

加select:

如果接收时,没有人发送,会继续向下匹配,如n := <-c1。如果发送,没有人接收,会继续向下匹配如c3 <- 1。

如果全部没有匹配,会睡眠等待,直到有匹配项过来。如果有default,则走default,不会睡眠等待。

如果接收时,有人发送,匹配成功,进入case内语句执行后,继续下一轮匹配,如n := <-c1。

如果发送,有人接收,匹配成功,进入case内语句执行后,会继续向下匹配如c3 <- 1。

package main

import (
	"fmt"
	"math/rand"
	"time"
)

func generator() chan int {
	out := make(chan int)
	go func() {
		i := 0
		for {
			time.Sleep(
				time.Duration(rand.Intn(1500)) *
					time.Millisecond)
			out <- i
			i++
		}
	}()
	return out
}

func worker(id int, c chan int) {
	for n := range c {
		time.Sleep(time.Second)
		fmt.Printf("Worker %d received %d\n",
			id, n)
	}
}

func createWorker(id int) chan<- int {
	c := make(chan int)
	go worker(id, c)
	return c
}

func main() {
	var c1, c2 = generator(), generator()
	var worker = createWorker(0)

	var values []int
	tm := time.After(10 * time.Second)
	for {
		var activeWorker chan<- int
		var activeValue int
		if len(values) > 0 {
			activeWorker = worker
			activeValue = values[0]
		}

		select {
		case n := <-c1:
			values = append(values, n)
			fmt.Println("set values:", values)
		case n := <-c2:
			values = append(values, n)
			fmt.Println("set values:", values)
		case activeWorker <- activeValue:
			values = values[1:]
			fmt.Println("get values:", values)
		case <-tm:
			fmt.Println("bye")
			return
		}
	}
}

输出:

//刚开始没有c1和c2没有接受到数据时,由于activeWorker是nil chan,所以此时会睡眠,但在select的时候直接跳过。如果所有case都不匹配,那么就睡眠了。

第一轮:---睡眠等待---

之后每次匹配所有项,如果c1和c2有人写,activeWorker有值并且读端正在等待读,则执行case;如果都不匹配,那么就睡眠等待直到匹配,再执行下一轮循环把数据拿出来。

第二轮:set values: [0] //从c1获取数据
第三轮:get values: [] //activeWorker有值,向其中写入数据,并且读端正在等待读,此时可写入数据,执行case语句

第四轮:---睡眠等待---
第五轮:set values: [0] //从c2获取数据
第六轮:set values: [0 1]

第七轮:---睡眠等待---
第八轮:set values: [0 1 1]

第九轮:---睡眠等待---
第十轮:set values: [0 1 1 2]

第十一轮:---睡眠等待---
Worker 0 received 0 //读端结束sleep,继续等待读状态,for n := range c {
第十二轮get values: [1 1 2] //activeWorker有值,向其中写入数据,并且读端正在等待读,此时可写入数据,执行case语句
set values: [1 1 2 2]
set values: [1 1 2 2 3]
Worker 0 received 0
get values: [1 2 2 3]
set values: [1 2 2 3 3]
set values: [1 2 2 3 3 4]
set values: [1 2 2 3 3 4 5]
Worker 0 received 1
get values: [2 2 3 3 4 5]
set values: [2 2 3 3 4 5 4]
set values: [2 2 3 3 4 5 4 5]
set values: [2 2 3 3 4 5 4 5 6]
set values: [2 2 3 3 4 5 4 5 6 6]
Worker 0 received 1
get values: [2 3 3 4 5 4 5 6 6]
set values: [2 3 3 4 5 4 5 6 6 7]
set values: [2 3 3 4 5 4 5 6 6 7 7]
Worker 0 received 2
get values: [3 3 4 5 4 5 6 6 7 7]
set values: [3 3 4 5 4 5 6 6 7 7 8]
set values: [3 3 4 5 4 5 6 6 7 7 8 9]
Worker 0 received 2
get values: [3 4 5 4 5 6 6 7 7 8 9]
set values: [3 4 5 4 5 6 6 7 7 8 9 8]
Worker 0 received 3
get values: [4 5 4 5 6 6 7 7 8 9 8]
set values: [4 5 4 5 6 6 7 7 8 9 8 10]
set values: [4 5 4 5 6 6 7 7 8 9 8 10 9]
Worker 0 received 3
get values: [5 4 5 6 6 7 7 8 9 8 10 9]
set values: [5 4 5 6 6 7 7 8 9 8 10 9 11]
set values: [5 4 5 6 6 7 7 8 9 8 10 9 11 10]
Worker 0 received 4
get values: [4 5 6 6 7 7 8 9 8 10 9 11 10]
set values: [4 5 6 6 7 7 8 9 8 10 9 11 10 11]
set values: [4 5 6 6 7 7 8 9 8 10 9 11 10 11 12]
bye

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

go routine channel select 的相关文章

  • SQL Server 的 SELECT JOIN 语句导致的死锁

    当执行带有两个表的 JOIN 的 SELECT 语句时 SQL Server 似乎 分别锁定语句的两个表 例如通过像这样的查询 这 SELECT FROM table1 LEFT JOIN table2 ON table1 id table
  • Mysql 选择行的总和

    我有一张包含以下信息的表 id Item ID stock package id amount price 0 775 1 16 0 22 1 758 2 10 0 28 2 775 3 10 0 30 3 774 4 10 0 25 4
  • 在基于mysql中的选择的插入期间增加非自动增量字段

    我使用 select 语句将一个表中的记录插入到另一个表中 插入的表有一个新字段 该字段应在每次更新时递增 1 但不应是自动递增字段 因为每次更新每组记录的数字都需要重新开始 如果使用的 select 语句选择 42 条记录 则新表将具有一
  • 在 PHP 中对逗号分隔值列表运行选择

    我在数据库上运行选择查询时遇到一些问题 一些数据以逗号分隔值的列表形式保存 例如 Table example tbl Id People Children 1 1 2 3 8 10 3 2 7 6 12 18 19 2 我正在尝试运行的示例
  • 将多个值插入隐藏字段

    我有一个选择列表 您可以在其中选择多个城市 选择城市时 我想将邮政编码添加到隐藏字段 我现在的解决方案将值插入到隐藏字段 但是 当 fx 时它会覆盖该值 单击一个新城市 它应该只附加到值中 例如 value value1 value2 va
  • SQL 连接具有特定条件的两个表

    表A结构 表B结构 上面是两个表 TableB TableARelationID是一个relationID 用于映射表A 期望的输出 期望的结果将采用 TableA RecordID 和 TableB Text 但仅采用表 B 中的类型 2
  • 跳出选择循环?

    我正在尝试使用select在循环中接收消息或超时信号 如果收到超时信号 则循环应中止 package main import fmt time func main done time After 1 time Millisecond num
  • 将 SELECT DISTINCT ON 查询从 Postgresql 转换为 MySQL

    我一直在使用PostgreSQL现在迁移到MySQL 在我的查询中 我正在使用PostgreSQL s SELECT DISTINCT ON col1 col2 col3 我想知道这句话是否有对应的内容MySQL 没有完全等效的方法可以将使
  • 将一项选择中的两项计数相除

    我有一个这样的表 date timestamp Error integer someOtherColumns 我有一个查询来选择特定日期的所有行 SELECT from table WHERE date date 2010 01 17 现在
  • with(nolock) 或 (nolock) - 有区别吗?

    一切都基于 with nolock 完全适合这种情况的假设 已经有很多问题在争论是否使用 with nolock 我环顾四周 无法找到使用之间是否存在实际差异with nolock select customer zipcode from
  • 如何在选择查询中创建新列

    在 MS Access 中 我想将新列插入到选择查询的返回结果中 新列的每一行都具有相同的值 例如 我的选择返回列 A B 我希望 C 成为选择查询创建的新列 A B C a1 b1 c a2 b2 c a3 b3 c select A B
  • Golang:我可以投射到 chan 接口吗{}

    我正在尝试为订阅编写一个通用包装器 例如 type Subscriber interface Subscribe addr string chan interface 假设有一个我想使用的库 其中有一个 subscribe 方法 但它使用c
  • MySQL 错误:#1142 - SELECT 命令被拒绝给用户

    我在一台服务器上的某个查询时遇到问题 在我测试过的所有其他地方 它工作得很好 但在我想使用它的服务器上 它不起作用 这是关于以下 SQL SELECT facturen id AS fid projecten id AS pid titel
  • 如果通过 SQL 查询结果没有找到记录,则应为 0

    我正在使用火鸟 我需要以下结果 但我没有得到我需要的结果 我尝试了以下查询 SELECT CASE EXTRACT MONTH FROM pd Date WHEN 1 THEN January WHEN 2 THEN February WH
  • Python 3:如何更改GDAL中的图像数据?

    我有一个 GeoTIFF 图像 其中包含颜色表和带有 8 位表键的单个栅格带 并且使用 LZW 压缩 我加载该图像gdal Open https gdal org python osgeo gdal module html 我还有一个包含
  • 在 BEFORE INSERT 触发器中使用 IF EXISTS (SELECT ...) (Oracle)

    我的代码不起作用 Oracle 告诉我创建触发器时出现构建错误 显然我无法获得有关构建错误的更准确信息 我以前确实没有做过很多SQL 所以我对语法不太熟悉 我有一种预感 Oracle 不喜欢我的 IF EXISTS SELECT THEN
  • MySQL #1093 - 您无法在 FROM 子句中指定用于更新的目标表“赠品”

    I tried UPDATE giveaways SET winner 1 WHERE ID SELECT MAX ID FROM giveaways 但它给出了 1093 您无法指定目标表 赠品 进行更新FROM clause 本文 ht
  • SQL Server - 将行连接到逗号分隔的列表中

    假设我有一个临时表 如下所示 Id Value 1 1 1 2 1 3 2 1 2 2 我希望我的桌子是这样的 Id ValueList 1 1 2 3 2 1 2 所以基本上我需要将我的值分组为逗号分隔的列表 我已经尝试过以下操作 SEL
  • 何时在 mysql 中使用 Union [关闭]

    Closed 这个问题需要多问focused help closed questions 目前不接受答案 对于 Union 您会在什么现实情况下使用它 因为对我来说 对具有不同列用途 含义的两个表中的两个选择查询使用联合是没有意义的 例如
  • chrome 中选择选项元素的额外填充

    我有一个选择元素 用户可以在其中选择分类和描述 仅在 Chrome 浏览器中 我有一个额外的填充 无法使用 padding 0 或其他 css 标签删除它 Chrome 的屏幕 https i stack imgur com m3iIb p

随机推荐