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

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逻辑核心上实现并行的效果,这里举个例子:

Go 复制代码
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)
}
Go 复制代码
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的时候需要为其指定元素类型。

Go 复制代码
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,先接受才可以发送

Go 复制代码
package day12

import "fmt"

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

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

Go 复制代码
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有缓冲通道

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

Go 复制代码
package day12

import "fmt"

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

2.5 从通道取值

Go 复制代码
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)
	}
}
相关推荐
懒大王爱吃狼39 分钟前
Python教程:python枚举类定义和使用
开发语言·前端·javascript·python·python基础·python编程·python书籍
秃头佛爷2 小时前
Python学习大纲总结及注意事项
开发语言·python·学习
待磨的钝刨2 小时前
【格式化查看JSON文件】coco的json文件内容都在一行如何按照json格式查看
开发语言·javascript·json
XiaoLeisj4 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
dayouziei4 小时前
java的类加载机制的学习
java·学习
励志成为嵌入式工程师5 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉5 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer5 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq5 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
记录成长java7 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet