并发:
因为是在一个cpu上,比如有10个线程,每个线程执行10毫秒(进行轮询操作),从人的角度看,好像这10个线程都在运行,但是从微观上看,在某一个时间点看,其实只有一个线程在执行,这就是并发
并行:
因为是在多个cpu上(比如有10个cpu),比如有10个线程,每个线程执行10毫秒(各自在不同cpu上执行),从人的角度看,这10个线程都在运行,但是从微观上看,在某一个时间点看,也同时有10个线程在执行,这就是并行
go主线程(可以称为线程/也可以理解成进程):一个go线程上,可以起多个协程,协程是轻量级的线程【编译器做优化】
package main
import (
"fmt"
"strconv"
"time"
)
func test() {
for i := 1; i <= 10; i++ {
fmt.Println("test() hello, world " + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
func main() {
go test() //开启了一个协程
for i := 1; i <= 10; i++ {
fmt.Println("main() hello, golang " + strconv.Itoa(i))
time.Sleep(time.Second)
}
}
说明:
为了充分利用多cpu的优势,在golang程序中,设置运行的cpu数目
package main
import (
"fmt"
"runtime"
)
func main() {
//获取当前系统cpu的数量
num := runtime.NumCPU()
//设置num-1的cpu运行go程序
runtime.GOMAXPROCS(num)
fmt.Println("num=", num)
}
计算1-200的各个数的阶乘,并且把各个数的阶乘放入到map中。最后显示出来。使用goroutine完成
package main
import (
"fmt"
"time"
)
//1.编写一个函数,计算各个数的阶乘,并放入到map中
//2. 启动的多个协程,统计的将结果放入到map中
//3. map应该做出一个全局的
var (
myMap = make(map[int]int, 10)
)
//test函数就是计算n!,将这个结果放入到myMap
func test(n int) {
res := 1
for i := 1; i <= n; i++ {
res *= i
}
myMap[n] = res
}
func main() {
//开启多个协程完成这个任务[200个]
for i := 1; i <= 200; i++ {
go test(i)
}
//休眠10秒
time.Sleep(time.Second * 10)
for i, v := range myMap {
fmt.Printf("map[%d]=%d\n", i, v)
}
}
# 启用数据数据竞争检测
go build -race main.go
package main
import (
"fmt"
"sync"
"time"
)
//1.编写一个函数,计算各个数的阶乘,并放入到map中
//2. 启动的多个协程,统计的将结果放入到map中
//3. map应该做出一个全局的
var (
myMap = make(map[int]int, 10)
//声明一个全局的互斥锁
//lock 是一个全局的互斥锁
//sync 是包: synchornized 同步
//Mutex : 是互斥
lock sync.Mutex
)
//test函数就是计算n!,将这个结果放入到myMap
func test(n int) {
res := 1
for i := 1; i <= n; i++ {
res += i
}
//res放入到myMap
//加锁
lock.Lock()
myMap[n] = res
//解锁
lock.Unlock()
}
func main() {
//开启多个协程完成这个任务[200个]
for i := 1; i <= 200; i++ {
go test(i)
}
//休眠10秒
time.Sleep(time.Second * 10)
lock.Lock()
for i, v := range myMap {
fmt.Printf("map[%d]=%d\n", i, v)
}
lock.Unlock()
}
var 变量名 chan 数据类型
var intChan chan int
var mapChan chan map[int]string
var perChan chan Person
var perChan2 chan *Person
说明:
channel是引用类型
channel必须初始化才能写入数据,即make后才能使用
管道是有类型的,intChan 只能写入整数int
package main
import "fmt"
func main() {
//1.创建一个可以存放3个int类型的管道
var intChan chan int
intChan = make(chan int, 3)
fmt.Printf("intChan 的值=%v intChan 本身的地址=%p\n", intChan, &intChan)
intChan <- 10
num := 211
intChan <- num
intChan <- 50
<- intChan //从管道取出数据可以不接收,就扔掉了
//intChan <- 98 //给管道写入数据时,不能超过其容量
//管道的长度和cap(容量)
fmt.Printf("channel len=%v cap=%v\n", len(intChan), cap(intChan))
var num2 int
num2 = <- intChan
fmt.Println("num2=", num2)
fmt.Printf("channel len=%v cap=%v \n", len(intChan), cap(intChan))
//在没有使用协程的情况下,如果管道数据已经全部取出,再取就会报 deadlock
num3 := <- intChan
num4 := <- intChan
//num5 := <- intChan
fmt.Println("num3=", num3, "num4=", num4)
}
package main
import "fmt"
func main() {
var intChan chan int
intChan = make(chan int, 3)
intChan <- 10
intChan <- 20
intChan <- 10
num1 := <- intChan
num2 := <- intChan
num3 := <- intChan
fmt.Printf("num1=%v num2=%v num3=%v \n", num1, num2, num3)
}
package main
import "fmt"
func main() {
var mapChan chan map[string]string
mapChan = make(chan map[string]string, 10)
m1 := make(map[string]string, 20)
m1["city1"] = "北京"
m1["city2"] = "天津"
m2 := make(map[string]string, 20)
m2["hero1"] = "宋江"
m2["hero2"] = "武松"
mapChan <- m1
mapChan <- m2
m3 := make(map[string]string, 20)
m4 := make(map[string]string, 20)
m3 = <- mapChan
m4 = <- mapChan
fmt.Printf("mapChan=%v\nm1=%v\nm2=%v\nm3=%v\nm4=%v\n", mapChan, m1, m2, m3, m4)
}
package main
import "fmt"
type Cat struct {
Name string
Age int
}
func main() {
var catChan chan Cat
catChan = make(chan Cat, 10)
cat1 := Cat{
Name: "tom",
Age: 10,
}
cat2 := Cat{
Name: "jack",
Age: 80,
}
catChan <- cat1
catChan <- cat2
cat11 := <- catChan
cat22 := <- catChan
fmt.Println(cat11, cat22)
}
package main
import "fmt"
type Cat struct {
Name string
Age int
}
func main() {
var catChan chan *Cat
catChan = make(chan *Cat, 10)
cat1 := Cat{
Name: "tom",
Age: 10,
}
cat2 := Cat{
Name: "jack",
Age: 180,
}
catChan <- &cat1
catChan <- &cat2
cat11 := <- catChan
cat22 := <- catChan
fmt.Println(cat11, cat22)
}
package main
import "fmt"
type Cat struct {
Name string
Age int
}
func main() {
var allChan chan interface{}
allChan = make(chan interface{}, 10)
cat1 := Cat{
Name: "tom",
Age: 18,
}
cat2 := Cat{
Name: "jack",
Age: 180,
}
allChan <- cat1
allChan <- cat2
allChan <- 10
allChan <- "mary"
cat11 := <- allChan
cat22 := <- allChan
v1 := <- allChan
v2 := <- allChan
fmt.Println(cat11, cat22, v1, v2)
}
package main
import "fmt"
type Cat struct {
Name string
Age int
}
func main() {
var allChan chan interface{}
allChan = make(chan interface{}, 10)
cat1 := Cat{
Name: "tom",
Age: 18,
}
cat2 := Cat{
Name: "jack",
Age: 180,
}
allChan <- cat1
allChan <- cat2
allChan <- 10
allChan <- "mary"
cat11 := <- allChan
fmt.Printf("cat11=%T, cat11=%v\n", cat11, cat11)
//使用类型断言
a := cat11.(Cat)
fmt.Printf("cat11.Name=%v\n", a.Name)
}
使用内置函数close可以关闭channel,当channel关闭后,就不能再向channel写数据了,但是仍然可以从channel读取数据
package main
import "fmt"
func main() {
intChan := make(chan int, 3)
intChan <- 100
intChan <- 200
close(intChan) //关闭channel,不能再写了
fmt.Println("ok")
n1 := <- intChan
fmt.Println("n1=", n1)
}
func close(c chan<- Type)
内建函数close关闭信道,该通道必须为双向的或只发送的。它应当只由发送者执行,而不应由接收者执行,其效果是在最后发送的值被接收后停止该通道。在最后的值从已关闭的信道中被接收后,任何对其的接收操作都会无阻塞的成功。对于已关闭的信道,语句:
x, ok := <-c
还会将ok置为false
channel支持 for-range 的方式进行遍历
package main
import "fmt"
func main() {
intChan2 := make(chan int, 100)
for i := 0; i < 100; i++ {
intChan2 <- i * 2
}
close(intChan2)
for v := range intChan2 {
fmt.Println("v=", v)
}
}
goroutine和channel协同工作
package main
import "fmt"
//write Data
func writeData(intChan chan int) {
for i := 1; i <= 50; i++ {
//放入数据
intChan <- i
fmt.Println("writeData ", i)
}
close(intChan)
}
//read Data
func readData(intChan chan int, exitChan chan bool) {
for {
v, ok := <- intChan
if !ok {
break
}
fmt.Printf("readData 读到数据=%v\n", v)
}
exitChan <- true
close(exitChan)
}
func main() {
intChan := make(chan int, 50)
exitChan := make(chan bool, 1)
go writeData(intChan)
go readData(intChan, exitChan)
for {
_, ok := <- exitChan
if !ok {
break
}
}
}
func main() {
intChan := make(chan int, 10)
exitChan := make(chan bool, 1)
go writeData(intChan)
//go readData(intChan, exitChan)
for _ = range exitChan {
fmt.Println("ok...")
}
}
如果只是向管道写入数据 ,而没有读取,就会出现阻塞而deadlock,写管道和读管道的频率不一致,无所谓
统计1-200000的数字中,哪些是素数?
思路:
package main
import (
"fmt"
"time"
)
//向intChan 放入1-8000个数
func putNum(intChan chan int) {
for i := 1; i <= 8000; i++ {
intChan <- i
}
close(intChan)
}
//从intChan 取出数据,并判断是否为素数,如果是,就
//放入到primeChan
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {
var flag bool
for {
time.Sleep(time.Millisecond * 10)
num, ok := <- intChan
if !ok {//intChan 取不到
break
}
flag = true //假设是素数
//判断num是不是素数
for i := 2; i < num; i++ {
if num % i == 0 {
flag = false
break
}
}
if flag {
//是素数,将这个数放入到primeChan
primeChan <- num
}
}
fmt.Println("有一个primeNum协程因为取不到数据,退出")
//这里还不能关闭primeChan
exitChan <- true
}
func main() {
intChan := make(chan int, 1000)
primeChan := make(chan int, 2000)
//标识退出的管道
exitChan := make(chan bool, 4)
//开启一个协程,向intChan放入1-8000个数
go putNum(intChan)
//开启4个线程,从intChan 取出数据,并判断是否为素数,如果是,就放入primeChan
for i := 0; i < 4; i++ {
go primeNum(intChan, primeChan, exitChan)
}
//主线程
go func() {
for i := 0; i < 4; i++ {
<- exitChan
}
//当从exitChan取出了4个结果,就可以关闭primeChan
close(primeChan)
}()
//变量primeChan,把结果取出
for {
res, ok := <- primeChan
if !ok {
break
}
fmt.Printf("素数=%d\n", res)
}
fmt.Println("main 线程退出")
}
package main
import "fmt"
func main() {
//默认情况下,管道是双向的(可读写)
//管道声明为只写
var chan2 chan<- int
chan2 = make(chan int, 3)
chan2 <- 20
//num := <- chan2 //error
fmt.Println("chan2=", chan2)
//声明为只读
var chan3 <-chan int
num2 := <- chan3
//chan3 <- 30 //error
fmt.Println("num2", num2)
}
package main
import "fmt"
func send(ch chan<- int, exitChan chan struct{}) {
for i := 0; i < 10; i++ {
ch <- i
}
close(ch)
var a struct{}
exitChan <- a
}
func recv(ch <-chan int, exitChan chan struct{}) {
for {
v, ok := <- ch
if !ok {
break
}
fmt.Println(v)
}
var a struct{}
exitChan <- a
}
func main() {
var ch chan int
ch = make(chan int, 10)
exitChan := make(chan struct{}, 2)
go send(ch, exitChan)
go recv(ch, exitChan)
var total = 0
for _ = range exitChan {
total++
if total == 2 {
break
}
}
fmt.Println("结束...")
}
package main
import (
"fmt"
"time"
)
func main() {
//定义一个管道10个数据 int
intChan := make(chan int, 10)
for i := 0; i < 10; i++ {
intChan <- i
}
//定义一个管道 5个数据 string
stringChan := make(chan string, 5)
for i := 0; i < 5; i++ {
stringChan <- "hello" + fmt.Sprintf("%d", i)
}
//在实际开发中,可能不好确定什么时候关闭该管道,可以使用select方式解决
for {
select {
// 如果intChan一直没有关闭,不会一直阻塞,而会自动到下一个case匹配
case v := <- intChan:
fmt.Printf("从intChan读取的数据%d\n", v)
time.Sleep(time.Second)
case v := <- stringChan:
fmt.Printf("从stringChan读取的数据%s\n", v)
time.Sleep(time.Second)
default:
fmt.Printf("都取不到了")
time.Sleep(time.Second)
return
}
}
}
package main
import (
"fmt"
"time"
)
func sayHello() {
for i := 0; i < 10; i++ {
time.Sleep(time.Second)
fmt.Println("hello, world")
}
}
func test() {
//使用defer + recover
defer func() {
//捕获test抛出的panic
if err := recover(); err != nil {
fmt.Println("test() 发生错误", err)
}
}()
var myMap map[int]string
myMap[0] = "golang" //error
}
func main() {
go sayHello()
go test()
for i := 0; i < 10; i++ {
fmt.Println("main() ok=", i)
time.Sleep(time.Second)
}
}