go语言基础学习(九)--并发篇

本文探讨了Go语言中如何通过GOMAXPROCS调整并发线程数,以及goroutine和channel在并发执行中的作用,重点讲解了无缓冲和有缓冲通道的同步机制。
摘要由CSDN通过智能技术生成

1.runtime包 (续)

runtime.GOMAXPROCS

Go运行时的调度器使用GOMAXPROCS参数来确定需要使用多少个OS线程来同时执行Go代码。默认值是机器上的CPU核心数。例如在一个8核心的机器上,调度器会把Go代码同时调度到8个OS线程上(GOMAXPROCS是m:n调度中的n)。

Go语言中可以通过runtime.GOMAXPROCS()函数设置当前程序并发时占用的CPU逻辑核心数。

Go1.5版本之前,默认使用的是单核心执行。Go1.5版本之后,默认使用全部的CPU逻辑核心数。

我们可以通过将任务分配到不同的CPU逻辑核心上实现并行的效果,这里举个例子:

package main

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

func a(i int) {
	fmt.Println("A", i)
}

func b(i int) {
	fmt.Println("B", i)
}
func main() {
	runtime.GOMAXPROCS(1)
	for i := 0; i < 5; i++ {
		go a(i)
		go b(i)
	}

	time.Sleep(time.Second * 2)
}

 

 

package main

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

func a(i int) {

	fmt.Println("A", i)

}

func b(i int) {
	fmt.Println("B", i)
}
func main() {
    // 逻辑核心数设置为2
	runtime.GOMAXPROCS(2)
	for i := 0; i < 10; i++ {
		go a(i)
		go b(i)
	}

	time.Sleep(time.Second * 2)
}

可以看到兩種結果的對比,第一种依然按照函数的顺序执行,BABABA交叉执行,第二种当程序分配了两个内核之后,执行顺序就会是并行状态。

Go语言中的操作系统线程和goroutine的关系:

  • 1.一个操作系统线程对应用户态多个goroutine。
  • 2.go程序可以同时使用多个操作系统线程。
  • 3.goroutine和OS线程是多对多的关系,即m:n。

 2.Channel-协程间的通信机制

单纯地将函数并发执行是没有意义的。函数与函数间需要交换数据才能体现并发执行函数的意义。

虽然可以使用共享内存进行数据交换,但是共享内存在不同的goroutine中容易发生竞态问题。为了保证数据交换的正确性,必须使用互斥量对内存进行加锁,这种做法势必造成性能问题。

Go语言的并发模型是CSP(Communicating Sequential Processes),提倡通过通信共享内存而不是通过共享内存而实现通信。

如果说goroutine是Go程序并发的执行体,channel就是它们之间的连接。channel是可以让一个goroutine发送特定值到另一个goroutine的通信机制

Go 语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明channel的时候需要为其指定元素类型。

 

package day12

import "fmt"

func D121() {
	// 建立channel变量
	var ch chan int //声明一个传递int型的通道
	var ch1 chan string
	var ch2 chan []int // 切片类型
	fmt.Println("空channel的值为:", ch)
	// 使用make初始化
	// make(chan 元素类型, [缓冲大小])
	chn := make(chan int)
	// 发送
	chn <- 10
	// 接收
	tmp := <-chn
	// 关闭通道
	close(chn)
}

 2.3无缓冲通道 (同步通道)

对于一个无缓冲的通道,可以看成是os的PV操作,只有先P才可以V,先接受才可以发送

package day12

import "fmt"

func D122() {
	ch1 := make(chan int)
	ch1 <- 10
	fmt.Println(ch1)
}

 

 goroutine启动接收才可以使用无缓冲通道发送

package day12

import "fmt"

func D122() {
	ch1 := make(chan int)
	// 启用goroutine从通道接收值
	go recv(ch1)
	ch1 <- 10
	fmt.Println("发送成功", &ch1)
}

func recv(c chan int) {
	a := <-c
	fmt.Println("接收成功", a)
}

无缓冲通道上的发送操作会阻塞,直到另一个goroutine在该通道上执行接收操作,这时值才能发送成功,两个goroutine将继续执行。相反,如果接收操作先执行,接收方的goroutine将阻塞,直到另一个goroutine在该通道上发送一个值。

使用无缓冲通道进行通信将导致发送和接收的goroutine同步化。因此,无缓冲通道也被称为同步通道。

 

 2.4有缓冲通道

 就是通道有了缓冲容量,遵循队列先进先出

package day12

import "fmt"

func D123() {
	ch1 := make(chan int, 1)
	ch1 <- 20
	fmt.Println("缓冲通道大小为1,发送成功")
}

2.5 从通道取值

package day12

import "fmt"

func D124() {
	// 声明无缓冲通道
	ch1 := make(chan int)
	ch2 := make(chan int)
	// 开启goroutine将0~100的数发送到ch1中
	go func() {

		for i := 0; i < 100; i++ {
			ch1 <- i
		}
		close(ch1)
	}()

	go func() {
		// 开启goroutine从ch1中接收值,并将该值的平方发送到ch2中
		for {
			i, ok := <-ch1 //通道关闭后再取值ok=false
			if !ok {
				fmt.Println("通道关闭")
				break
			}
			ch2 <- i * i
		}
		close(ch2)
	}()

	// 在主goroutine中从ch2中接收值打印
	for i := range ch2 { // 通道关闭后会退出for range循环
		fmt.Println(i)
	}
}

  • 23
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值