Go select chan操作

摘要1:原文go select的用法 - 梁天 - 博客园golang中的select语句格式如下 select { case <-ch1: // 如果从 ch1 信道成功接收数据,则执行该分支代码 case ch2 <- 1: // 如果成功向https://www.cnblogs.com/gwyy/p/13629999.html

摘要2:Go-select语句详解_Chasel_H-CSDN博客_go select 语句

摘要3:GO学习-(36) Go语言在select语句中实现优先级 - 小学弟- - 博客园

golang中的select语句格式如下

select {
    case <-ch1:
        // 如果从 ch1 信道成功接收数据,则执行该分支代码
    case ch2 <- 1:
        // 如果成功向 ch2 信道成功发送数据,则执行该分支代码
    default:
        // 如果上面都没有成功,则进入 default 分支处理流程
}

可以看到select的语法结构有点类似于switch,但又有些不同。

select里的case后面并不带判断条件,而是一个信道的操作,不同于switch里的case,对于从其它语言转过来的开发者来说有些需要特别注意的地方。

golang 的 select 就是监听 IO 操作,当 IO 操作发生时,触发相应的动作每个case语句里必须是一个IO操作,确切的说,应该是一个面向channel的IO操作。

注:Go 语言的 select 语句借鉴自 Unix 的 select() 函数,在 Unix 中,可以通过调用 select() 函数来监控一系列的文件句柄,一旦其中一个文件句柄发生了 IO 动作,该 select() 调用就会被返回(C 语言中就是这么做的),后来该机制也被用于实现高并发的 Socket 服务器程序。Go 语言直接在语言级别支持 select关键字,用于处理并发编程中通道之间异步 IO 通信问题。

注意:如果 ch1 或者 ch2 信道都阻塞的话,就会立即进入 default 分支,并不会阻塞。但是如果没有 default 语句,则会阻塞直到某个信道操作成功为止。

知识点

  1. select语句只能用于信道的读写操作
  2. select中的case条件(非阻塞)是并发执行的,select会选择先操作成功的那个case条件去执行,如果多个同时返回,则随机选择一个执行,此时将无法保证执行顺序。对于阻塞的case语句会直到其中有信道可以操作,如果有多个信道可操作,会随机选择其中一个 case 执行
  3. 对于case条件语句中,如果存在信道值为nil的读写操作,则该分支将被忽略,可以理解为从select语句中删除了这个case语句
  4. 如果有超时条件语句,判断逻辑为如果在这个时间段内一直没有满足条件的case,则执行这个超时case。如果此段时间内出现了可操作的case,则直接执行这个case。一般用超时语句代替了default语句
  5. 对于空的select{},会引起死锁
  6. 对于for中的select{}, 也有可能会引起cpu占用过高的问题

select 实例1:

package main

import (
	"fmt"
	"log"
	"runtime"
	"time"
)

func main() {
	log.Println("111")
	ch1 := make(chan int)
	ch2 := make(chan bool)

	// go test1(ch1)
	// go test2(ch2)

	go func() {
		for i := 0; i < 10; i++ {
			ch1 <- i
		}
		close(ch1)

		ch2 <- true
		runtime.Goexit()  // 退出子协程
	}()

	for {
		select {
		case n := <-ch1:
			fmt.Println(n)
		case <-ch2:
			// break  // 跳出select
			// return    // 退出整个程序
			goto Exit  // 跳转到程序定义的标签位置
		}

	}

Exit:
	fmt.Println("主协程结束")

}

注意:主协程不能使用`runtime.Goexit()`

空select:

package main

func main() {
    select {
                    // 永久阻塞
    }

    fmt.Println("主程序退出!")  // 永远不会执行到这个位置
}

总结:

  • select 不存在 任何case:永久阻塞当前goroutine
  • select 只存在一个case:阻塞的发送/接收
  • select 存在多个case:随机选择一个满足条件的case执行
  • select 存在default,其他case都不满足条件时:执行default语句中的代码

select优先级:

package main

import (
	"fmt"
	"log"
	"time"
)


// 优先级示例
func worker(ch1, ch2 <-chan int, stopCh chan struct{}) {
	for {
		select {
		case <-stopCh:
			return
		case job1 := <-ch1:
			fmt.Println(job1) // 优先处理job1
		case job2 := <-ch2:
		priority:
			for {
				select {
				case job1 := <-ch1:
					fmt.Println(job1) // 优先处理job1
				default:
					break priority
				}
			}
			fmt.Println(job2) // 当ch1通道没有数据时,才处理job2
		}
	}
}

func main() {
	log.Println("111")
	ch1 := make(chan int)
	ch2 := make(chan int)
	ch3 := make(chan struct{})

	go worker(ch1, ch2, ch3)

	go func() {
		for i := 0; i < 10; i++ {
			ch1 <- i
		}
		// close(ch1)

	}()

	go func() {
		for i := 0; i < 10; i++ {
			time.Sleep(time.Millisecond * 100) // 休眠100毫秒
			ch2 <- i + 100
		}
		// close(ch2)

	}()

	time.Sleep(time.Second * 5)
	ch3 <- struct{}{}

}

你可能感兴趣的:(golang,golang,开发语言,后端)