解决程序同步问题
package main
import (
"fmt"
"sync"
"time"
)
//需求:现在要计算1-200的各个数的阶乘,并且把各个数的阶乘放入到map中。
//最后显示出来。要求使用goroutine完成
//思路:
//1,编写一个函数,来计算各个数的阶乘,并放入到map中.
//2.我们启动的协程多个,统计的将结果放入到 map 中
//3. map 应该做出一个全局的
var (
myMap = make(map[int]int, 10)
//声明全局互斥锁
//lock 是一个全局的互斥锁
//sync 是包:synchronized
//Mutex 是互斥
lock sync.Mutex
)
func test(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 <= 20; i++ {
go test(i)
}
//休眠
time.Sleep(time.Second * 10)
lock.Lock()
//遍历结果
for i, v := range myMap {
fmt.Printf("map[%d] = %d \n", i, v)
}
lock.Unlock()
}
前面使用全局变量加锁同步来解决goroutine的通讯,但不完美
package main
import "fmt"
func main() {
//1.创建一个可以存放3个int类型的管道
var intChan chan int
intChan = make(chan int, 3)
//intChan 是什么?
fmt.Printf("intChan的值=%v intChan本身的地址=%v\n", intChan, &intChan)
//3.向管道写入数据
intChan <- 10
num := 200
intChan <- num
intChan <- 100
//注意:
//写入数据时,不能超过容量
//4.看看管道的长度和容量
fmt.Printf("chanel len = %v cap = %v\n", len(intChan), cap(intChan))
//5.从管道中去读数据
var num2 int
num2 = <-intChan
fmt.Println("num2=", num2)
fmt.Printf("chanel len = %v cap = %v\n", len(intChan), cap(intChan))
//6.在没有使用协程的情况下,如果我们的管道数据已经全部取出,再取就会报告 deadlock
}
package main
import "fmt"
type Cat struct {
Name string
Age int
}
func main() {
//定义一个存放任意数据类型的管道
allChan := make(chan interface{}, 3)
allChan <- 10
allChan <- "tom"
cat := Cat{"描", 10}
allChan <- cat
//我们希望获得管道中的第三个元素,则将前两个推出
<-allChan
<-allChan
newCat := <-allChan //从管道中取出的Cat是什么?
fmt.Printf("newCat=%T, newCat= %v \n", newCat, newCat)
a := newCat.(Cat) //类型断言
fmt.Printf("newCat.Name=%v\n", a.Name)
}
使用内置函数close可以关闭channel,当channel关闭后,就不能再向channel写数据了,但是仍然可以从该channel读取数据。
package main
import "fmt"
func main() {
intChan := make(chan int, 3)
intChan <- 300
intChan <- 200
close(intChan)
//这时不能写入数据到chanel
//管道关闭,读取数据是可以的
n1 := <-intChan
fmt.Println(n1)
}
channel支持for-rarge的方式进行遍历,请注意两个细节
1)在遍历时,如果channel没有关闭,则回出现deadlock的错误
2)在遍历时,如果channel已经关闭,则会正常遍历数据,遍历完后,就会退出遍历。
//遍历管道
intChan2 := make(chan int, 100)
for i := 0; i < 100; i++ {
intChan2 <- i * 2 //放入一百个数据
}
//在遍历时,如果channel没有关闭,则会出现deadlock的错误
//关闭管道
close(intChan2)
//遍历管道不能使用for 循环
for v := range intChan2 {
fmt.Println("v=", v)
}
package main
import (
"fmt"
)
//write data
func writeData(intChan chan int) {
for i := 1; i <= 50; i++ {
//放入数据
intChan <- i
fmt.Println("writeDate ", 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)
}
//readData 读取完数据后,完成任务
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
}
}
}
如果只是向管道写入数据,而没有读取,就会出现阻塞而dead lock
package main
import "fmt"
//想 intChan 放入 1-8000个数
func putNum(intChan chan int) {
for i := 0; i < 8000; i++ {
intChan <- i + 1
}
//关闭
close(intChan)
}
func primeNum(intChan chan int, primeChan chan int, exitChan chan bool) {
//使用for循环
//var num int
var flag bool //标识是否为素数
for {
num, ok := <-intChan
if !ok {
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 协程 因为取不到数据 ,退出")
//向退出的管道写入true
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
}
//关闭管道
close(primeChan)
}()
//遍历 primeNum
for {
res, ok := <-primeChan
if !ok {
break
}
fmt.Println("素数=", res)
}
fmt.Println("main退出")
}
package main
import "fmt"
func main() {
//管道可以声明为只读或者只写
//1. 在默认情况下,管道都是双向
//var chan1 chan int //双向,可读可写
//2. 声明为只写
var chan2 chan<- int
chan2 = make(chan int, 3)
chan2 <- 20
fmt.Println("chan2 = ", chan2)
//3.声明为只读
var chan3 <-chan int
num2 := <-chan3
fmt.Println("num2", num2)
}
package main
import "fmt"
func main() {
//使用select可以解决从管道取数据的阻塞问题
//1.定义一个管道 10 个数据int
intChan := make(chan int, 10)
for i := 0; i < 10; i++ {
intChan <- i
}
//2.定义一个管道 5 个数据 sting
stringChan := make(chan string, 5)
for i := 0; i < 5; i++ {
stringChan <- "hello" + fmt.Sprintf("%d", i)
}
//传统的方法在遍历管道时,如果不关闭会阻塞而导致deadlock
//问题,在实际开发中,听能我们不好确定什么关闭该管道.
//可以使用select方式可以解决
//label:
for {
select {
//注意:这里,如果intchan一直没有关闭,不会一直阻塞而deadlock-.
//会自动到下一个case匹配
case v := <-intChan:
fmt.Printf("从intChan读取的数据%d\n", v)
case v := <-stringChan:
fmt.Printf("从stringChan读取的数据 %s \n", v)
default:
fmt.Println("都去不到了...")
//break label
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)
}
}
本人博客地址