goroutine and channel

goroutine

Goroutine 是 Go 语言支持并发的核心,在一个Go程序中同时创建成百上千个goroutine是非常普遍的,一个goroutine会以一个很小的栈开始其生命周期,一般只需要2KB。区别于操作系统线程由系统内核进行调度, goroutine 是由Go运行时(runtime)负责调度。例如Go运行时会智能地将 m个goroutine 合理地分配给n个操作系统线程,实现类似m:n的调度机制,不再需要Go开发者自行在代码层面维护一个线程池。

在go语言中,每一个并发的执行单元叫做一个goroutine

并发:逻辑上具备同时处理多个任务的能力(一个cpu)

并行:物理上在同一时刻执行多个并发任务(多个cpu)

package main

import (
	"fmt"
	"sync"
	"time"
)

var mymap = make(map[int]int)

// 声明一个全局互斥锁
// lock 是一个全局互斥所

var lock sync.Mutex

func test1(n int) {
	res := 1
	for i := 1; i < n; i++ {
		res *= i
	}
	lock.Lock()
	mymap[n] = res
	lock.Unlock()

}
func main() {

	for i := 1; i < 21; i++ {
		go test1(i)
	}
	time.Sleep(5 * time.Second)
	lock.Lock()
	for i, v := range mymap {
		fmt.Println(i, v)
	}
	lock.Unlock()

}

channel

通道channel是用于 goroutine之间的数据传递,通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通信,避免了显式的锁机制。Go 语言中的通道(channel)是一种特殊的类型。通道像一个传送带或者队列,总是遵循先入先出(First In First Out)的规则,保证收发数据的顺序。每一个通道都是一个具体类型的导管,也就是声明channel的时候需要为其指定元素类型。

package main

import "fmt"

// channle 本质就是一个数据结构-队列
// 数据是先进献出
// 线程安全
// channle是有类型的
// 声明
var chan1 = make(chan int)
var chan2 chan int

// 创建一个可以存放3个int类型的管道
var chan3 = make(chan int, 3)

// 向管道写入数据
func main() {
	chan3 <- 10
	num := 211
	chan3 <- num
	//看看管道的长度和容量
	fmt.Printf("长度%v,容量%v\n", len(chan3), cap(chan3))

	//写入数据不能超过容量
	//从管道中读取数据
	var num2 int
	num2 = <-chan3
	fmt.Println(num2)
	fmt.Printf("changdu%v,ronglian%v\n", len(chan3), cap(chan3))
}

selet

Go 语言内置了select关键字,使用它可以同时响应多个通道的操作。

Select 的使用方式类似于之前学到的 switch 语句,它也有一系列 case 分支和一个默认的分支。每个 case 分支会对应一个通道的通信(接收或发送)过程。select 会一直等待,直到其中的某个 case 的通信操作完成时,就会执行该 case 分支对应的语句。

package main

import "fmt"

// 用select解决从管道取数据的堵塞问题
// 传统的方法在遍历管道时,如果不关闭会阻塞而导致deadlock
// 问题 在实际开发中,可能我们不好确定什么时候关闭该管道
// 可以使用select方式来解决
func main() {
	intchan1 := make(chan int, 10)
	for i := 0; i < 10; i++ {
		intchan1 <- i
	}
	stringchan := make(chan string, 5)
	for i := 0; i < 5; i++ {
		stringchan <- "hello" + fmt.Sprint(i)
	}

	for {
		select {
		case v := <-intchan1:
			fmt.Printf("取到数据%的\n", v)
		case v := <-stringchan:
			fmt.Printf("sting取到%s\n", v)
		default:
			fmt.Printf("不玩了\n")
			return

		}
	}

}

实例

求1-8000中的素数

package main

import (
	"fmt"
	"math"
)

// 编译器运行,发现一个管道只有写没有读,则该管道会堵塞
// 判断是否为素数
func isPrime(n int) bool {
	if n <= 1 {
		return false
	}
	if n <= 3 {
		return true
	}
	if n%2 == 0 || n%3 == 0 {
		return false
	}
	sqrtN := int(math.Sqrt(float64(n)))
	for i := 5; i <= sqrtN; i += 6 {
		if n%i == 0 || n%(i+2) == 0 {
			return false
		}
	}
	return true
}
func testwrite(intChan chan int) {
	for i := 1; i <= 8000; i++ {
		intChan <- i
	}
	close(intChan)
}
func testread(intChan chan int, primeChan chan int, exitChan chan bool) {
	for v := range intChan {
		if isPrime(v) == true {
			primeChan <- v
		}
	}
	for {
		v, ok := <-intChan
		if !ok {
			break
		}
		fmt.Printf("read data from chan:%d\n", v)
	}
	exitChan <- true

}
func main() {
	intChan := make(chan int, 1000)
	primeChan := make(chan int, 10000)
	exitChan := make(chan bool, 4)
	go testwrite(intChan)
	for i := 1; i <= 4; i++ {
		go testread(intChan, primeChan, exitChan)
	}

	for i := 0; i < 4; i++ {
		<-exitChan
	}
	close(primeChan)
	close(exitChan)
	for v := range primeChan {
		fmt.Printf("read data from chan:%d\n", v)
	}

}

//使用协程后速度提高

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值