Go多线程|青训营笔记

37 阅读2分钟

这是我参与「第五届青训营 」笔记创作活动的第4天

协程简介

一个进程包含多个线程,线程是操作系统运行调度的基本单位。但是线程太占资源,且线程的切换会导致用户态和内核态的频繁转换,调度开销大。

goroutine是go语言中的一个轻量级线程,即协程,更高效、轻便、开销小。可运行上千万个并发。一个线程包含多个协程。

开启协程

采用go关键字便可开启一个协程

func main() {
   go thread1()
}

func thread1() {
   for i := 0; i < 10; i++ {
      fmt.Println("thread1打印", i)
   }
}

运行上述代码我们会发现,控制台什么也没有输出,因为,当一个goroutine启动后,主线程不知道goroutine什么时候结束,所以主线程结束的时候,goroutine可能还没执行完毕,就随着主线程main()关闭而被强制关闭了。

channel

因此,goroutine通过channel的方式来传递消息。

var channel chan int

func transmitMessage(methodName string) {
   for i := 0; i < 5; i++ {
      fmt.Printf("方法%v打印:%v\n", methodName, i)
   }
   channel <- 0
}
func main() {
   channel = make(chan int)
   go transmitMessage("a")
   go transmitMessage("b")
   fmt.Println("start")
   <-channel
   <-channel
}

image.png

channel只能通过make方法来创建,并且指定channel的传值类型。向channel插入数据时channel <- 数据 ,从channel取出数据通过 <-channel

Gosched()

通过runtime.Gosched()可以使当前goroutine放弃当前CPU执行权,进入CPU调度的就绪队列。如下,协程之间交叉打印。

var channel chan int

func transmitMessage(methodName string, msg int) {
   for i := 0; i < 5; i++ {
      fmt.Printf("方法%v打印:%v\n", methodName, i)
      runtime.Gosched()
   }
   channel <- msg
}
func main() {
   channel = make(chan int)
   go transmitMessage("a", 1)
   go transmitMessage("b", 2)
   fmt.Println("start")
   <-channel
   <-channel
}

image.png

select{}

当需要监听多个channel时,可使用select随机选择一个channel进行处理。

    case <-ch1 :     // 检测是否数据写入
    case ch2 <- 1 :  // 检测是否有数据可写
    default:
        // 如果以上都没有符合条件,那么进入default处理流程
}

随机选择一个channel读取,如下:

func main() {
   ch1 := make(chan int, 1) 
   ch1 <- 1                 //向通道ch1写入数据

   ch2 := make(chan int, 1)
   ch2 <- 2                 //向通道ch2写入数据

   select {
   case <-ch1:
      fmt.Println("ch1 read")
   case <-ch2:
      fmt.Println("ch2 read")
   }
}

image.png

func main()  {
	select {
	
	}
}

select空时,会引发死锁。 image.png

select{}应用:

典型应用,超时判断

//全局resChan来接受response,如果时间超过3S,
//resChan中还没有数据返回,则第二条case将执行
var returnChan = make(chan int)
// do request
func test() {
	select {
	case data := <-returnChan:
		getData(data)
	case <-time.After(time.Second * 3):
		fmt.Println("请求超时")
	}
}

func getData(data int) {
	//数据处理返回
}