go语言的并发详解编程语言

1、启动go语言的协程

package main 
 
import ( 
	"fmt" 
	"runtime" 
) 
 
//runtime包 
 
func main() { 
	//runtime.Gosched() 用于让出cpu时间片,让出这段cpu的时间片,让调度器重新分配资源 
 
	//写一个匿名函数 
	s := "test" 
	go func(s string) { 
		for i :=0;i <2;i++ { 
			fmt.Println(s) 
		} 
	}(s) 
 
	for i :=0;i <2;i ++ { 
		//如果代码跑到这里,调度器会把cpu资源释放出来,让调度器重新分配cpu资源,可以分配到子协程,也可以重新分配到主协程 
		runtime.Gosched() 
		fmt.Println("123") 
	} 
} 

  

2、runtime.Goexit()方法。立即终止当前的协程

package main 
 
import ( 
	"fmt" 
	"runtime" 
	"time" 
) 
 
//runtime.Goexit()   立即终止当前的协程 
func main() { 
	go func() { 
		defer fmt.Println("A.defter") 
		func () { 
			defer fmt.Println("B.defter") 
			//立即终止当前的协程,函数会走defer流程 
			runtime.Goexit() 
			fmt.Println("B") 
		}() 
		fmt.Println("A") 
	}() 
	for { 
		time.Sleep(2 * time.Second) 
	} 
} 
 
//不加runtime.Goexit()的结果 
//B 
//B.defter 
//A 
//A.defter 
 
//加runtime.Goexit()的结果 
//B.defter 
//A.defter 

  

3、runtime.GOMAXPROCS()表示go使用几个cpu执行代码

package main 
 
import ( 
	"fmt" 
	"runtime" 
) 
 
func main() { 
	//runtime.GOMAXPROCS() 表示让go用几个cpu做后面的事情 
	n := runtime.GOMAXPROCS(4) 
	fmt.Printf("%T--->%p---%d/n",n,n,n) 
	for { 
		go fmt.Print("0") 
		fmt.Print(1) 
	} 
} 

  

 4、管道定义和创建管道

package main 
 
import "fmt" 
 
//go语言的协程运行在相同的地址空间,因此访问共享内存必须做好同步,处理好线程安全问题 
 
//go语言的协程之间的通信通过协程间通信来共享内存,而不是共享内存来通信 
 
//channel是一个引用类型,用于多个协程间通信,内部实现了同步,确保并发安全 
 
 
//通道一般是结合协程一起使用 
 
 
//如果通道中没有数据,后面你还去取数据,则会报错 
//fatal error: all goroutines are asleep - deadlock! 
func main() { 
	//test45_1 := make(chan int) //定义一个无缓冲的通道 
 
	//无缓冲的通道是值在接受数据前没有任何能力保存数据,只能有一个数据进入通道,进入通道后,该通道就会加锁,一直到这个数据被取出,锁才释放 
 
	//无缓冲的通道有可能阻塞,如果我发送一个数据到通道,但是没有协程来取数据,则对于第一个协程就被阻塞 
 
	//test45_2 := make(chan int,10)  //定义 一个有缓冲的通道 
 
 
	//有缓冲的通道就是通道可以存储指定数量的数据,数据在里面也是有顺序的,但是如果缓冲的数量满了,这个通道也会是阻塞的 
 
	// 
	//test45_1 <- 10   //发送数据到通道 
	//<- test45_1      //接受通道中的数据,并丢弃 
	//x := <-test45_1 //从通道取值并赋值给x 
	//x,ok := <-test45_1  //ok检查通道是否关闭或者是否为空 
 
 
	//1、创建一个存放int类型的通道 
	test45_1 := make(chan int) 
 
	go func() { 
		defer fmt.Println("子协程结束") 
		fmt.Println("子协程正在运行") 
 
		test45_1 <- 111 
	}() 
	//<- test45_1 
	//主协程从通道中取数据 
	x := <- test45_1 
	fmt.Println(x) 
	fmt.Println("主协程结束") 
} 

  

5、管道的缓冲

package main 
 
import ( 
	"fmt" 
	"time" 
) 
 
func main() { 
	//无缓冲的通道,长度为0就可以了,有缓冲的通道,这里设置为非0就可以了 
	test46_1 := make(chan int,0) 
 
	//%P是打印内存地址,%T是打印变量的类型 
	//fmt.Printf("长度:%d--->容量:%d---->%P----%T",len(test46_1),cap(test46_1),test46_1,test46_1) 
 
	go func() { 
		defer fmt.Printf("子协程结束") 
 
		for i :=0;i < 3;i ++ { 
			fmt.Println("子协程插入数据") 
			test46_1 <- i 
			//fmt.Printf("长度:%d--->容量:%d---->%P----%T",len(test46_1),cap(test46_1),test46_1,test46_1) 
 
		} 
 
	}() 
 
	time.Sleep(2 * time.Second) 
	for j :=0;j <3;j++ { 
		fmt.Println("主协程取数据") 
		num := <- test46_1 
		fmt.Println(num) 
	} 
} 

  

6、关闭管道和接受关闭管道的信号

package main 
 
import "fmt" 
 
//close()方法,关闭通道的意思 
 
func main() { 
	test47_1 := make(chan int,4) 
 
	go func() { 
		for i :=0;i < 10;i ++ { 
			test47_1 <- i 
		} 
		//这个的意思关闭通道test47_1 
		close(test47_1) 
	}() 
 
	//for的写法,遍历通道 
	//for { 
	//	//子协程关闭了通道,这里ok就可以接收到,这里就可以走到bread流程,ok这个参数表示通道是否关闭 
	//	if data,ok := <- test47_1;ok { 
	//		fmt.Println(data) 
	//	}else { 
	//		break 
	//	} 
	//} 
 
	//range的写法,遍历通道 
	for data := range test47_1 { 
		fmt.Println(data) 
	} 
	fmt.Println("主协程结束") 
} 

  

7、只读管道和只写管道和生产者和消费者模型

package main 
 
import ( 
	"fmt" 
	"time" 
) 
 
//默认情况下,管道是双向的,既可以写入数据,也可以读出数据。go也可以定义单方向的管道,也就是说只发送数据,或者只写入数据 
 
//可以把双向的管道转换为单向的管道,但是不能把单向的管道转换为双向的管道 
 
//单方向的管道 
 
func producter(out chan <- int)  { 
	defer close(out) 
	for i := 0;i < 10;i++ { 
		out <- i 
	} 
 
} 
 
func consumer(int <-chan int){ 
	for num := range int { 
		fmt.Println(num) 
	} 
} 
func main() { 
 
	//1、定义管道 
	//定义一个正常的管道 
	//var test48_1 chan int 
 
	//定义一个单向的只写的管道 
	//var test48_2 chan <- float32 
 
	//定义一个单向的只读的管道 
	//var test48_3 <- chan int 
 
	//2、转换管道 
 
	//转换正常管道为只写或者只读的管道 
	//定义一个正常的管道 
	//test48_4 := make(chan int,3) 
 
	//把一个正常的管道转换为一个只写的管道 
	//var write_only chan <- int = test48_4 
 
	//把一个正常的管道转换为一个只读的管道 
	//var read_only <- chan int = test48_4 
 
 
 
	test48_5 := make(chan int,4) 
 
	//启动生产者 
	go producter(test48_5) 
 
	//启动消费者 
	consumer(test48_5) 
 
	time.Sleep(10 * time.Millisecond) 
	fmt.Println("down") 
 
} 

  

 8、Timer定时器

package main 
 
import ( 
	"fmt" 
	"time" 
) 
 
//定时器 
 
//time.NewTimer()。时间到了,只执行一次 
//time.NewTicker(),周期性的执行 
 
func main() { 
	//1、创建一个定时器,2s后定时器会将一个时间保存到一个C 
	test49_1 := time.NewTimer(2 * time.Second) 
 
	//打印系统当前的时间 
 
	t1 := time.Now() 
 
	fmt.Printf("t1----->%v/n",t1) 
 
	//从管道中取出C打印 
 
	t2 := <- test49_1.C 
	fmt.Printf("t2----->%v/n",t2) 
 
 
 
	//2、证明timer只执行一次 
	//test49_2 := time.NewTimer(4 * time.Second) 
	// 
	//for { 
	//	c := <- test49_2.C 
	//	fmt.Println(c) 
	//} 
 
	//3、通过timer实现一个延时的功能 
 
	//方式1 
	//time.Sleep(2 * time.Second) 
 
	//方式2 
	//test49_3 := time.NewTimer(2 * time.Second) 
 
	//方式3 
 
	//<-time.After(2 *time.Second) 
 
	//4、停止定时器 
	test49_4 := time.NewTimer(4 * time.Second) 
	//子协程 
	go func() { 
		//这个意思是3s后才能取出来数据 
		<- test49_4.C 
 
		fmt.Println("定时器时间到了") 
	}() 
 
	//关闭定时器 
	stop := test49_4.Stop() 
	if stop { 
		fmt.Println("定时器已经关闭") 
	} 
 
	//5、重置定时器 
	test49_5 := time.NewTimer(4 * time.Second) 
	//重置定时器为1s 
	test49_5.Reset(1 * time.Second) 
 
	for { 
 
	} 
	} 

  

9、ticker定时器和关闭ticker定时器

package main 
 
import ( 
	"fmt" 
	"time" 
) 
 
//time.NewTicker(),定时器,响应多次 
 
func main() { 
	//创建一个定时器,间隔1s 
	test50_1 := time.NewTicker(time.Second) 
	i := 0 
	go func() { 
		for { 
			c := <- test50_1.C 
			fmt.Println(c) 
			i ++ 
			fmt.Println(i) 
		} 
		}() 
 
 
	for { 
 
	} 
} 

  

10、select语句

package main 
 
import ( 
	"fmt" 
) 
 
//go语言提供select关键字,用来监听通道上的数据流动,语法和switch类似,区别是select必须要求每个case语句里必须是一个IO操作 
 
//如果都能匹配到,则随机选择一个通道去跑,select是比较随便的 
 
func main() { 
	//test51_1 := make(chan int,3) 
	//select { 
	//case <- test51_1: 
	//	fmt.Println("jja") 
	////如果从通道中可以读出数据,则执行这里 
	//case test51_1 <- 1: 
	//	fmt.Println("aa") 
	////如果通道中北写入数据,则执行号这里 
	//default: 
	//	fmt.Println("hah") 
	////如果上面都没成功,则执行这里 
	//} 
 
 
	test51_1 := make(chan int,1) 
	test51_2 := make(chan string,1) 
 
	go func() { 
		//time.Sleep(2 * time.Second) 
		test51_1 <- 1 
 
	}() 
	go func() { 
		test51_2 <- "Hello World" 
	}() 
 
	select { 
	case Value1:= <- test51_1: 
		fmt.Println(Value1) 
	case Value2 := <- test51_2: 
		fmt.Println(Value2) 
	} 
	fmt.Println("结束") 
 
} 

  

11、协程同步锁

package main 
 
import ( 
	"fmt" 
	"sync" 
	"time" 
) 
 
//go语言的协程同步锁,解决并发安全问题 
 
 
//取钱的例子 
 
type Account struct { 
	money int 
	flag sync.Mutex 
} 
 
func Check(a *Account)  { 
	time.Sleep(1 * time.Second) 
} 
 
func (a *Account)SetAccount(n int)  { 
	a.money = n 
} 
 
func (a *Account)GetAccount() (n int) { 
	return a.money 
} 
 
func (a *Account) buy1(n int)  { 
	a.flag.Lock() 
	if a.money > n { 
		Check(a) 
		a.money -= n 
	} 
	a.flag.Unlock() 
	fmt.Println(a.money) 
} 
 
 
func (a *Account) buy2(n int)  { 
	a.flag.Lock() 
	if a.money > n { 
		Check(a) 
		a.money -= n 
	} 
	a.flag.Unlock() 
	fmt.Println(a.money) 
} 
 
func main() { 
	var test52_1 Account 
	test52_1.SetAccount(10) 
 
	go test52_1.buy1(5) 
	go test52_1.buy2(6) 
	for { 
 
	} 
} 

  

12、wait

我们自己实现wait

package main 
 
import "fmt" 
//Add() 计数加1 
//Done() 计数减1 
//Wait() 主函数调用 
func main() { 
	test53_1 := make(chan int,2) 
	count := 2 
	go func() { 
		fmt.Println("子协程1") 
		test53_1 <- 1 
	}() 
	go func() { 
		fmt.Println("子协程2") 
		test53_1 <- 2 
	}() 
 
	for range test53_1 { 
		count -- 
		if count == 0 { 
			fmt.Println("所有的子协程都已经结束") 
			close(test53_1) 
		} 
	} 
} 

  

go语言为我们实现wait

package main 
 
import ( 
	"fmt" 
	"sync" 
) 
//Add() 计数加1 
//Done() 计数减1 
//Wait() 主函数调用 
func main() { 
 
	var wait_group sync.WaitGroup 
 
	//这里就是子协程的个数 
	wait_group.Add(2) 
	//test54_1 := make(chan int,2) 
 
	go func() { 
		fmt.Println("子协程1") 
		wait_group.Done() 
	}() 
	go func() { 
		fmt.Println("子协程2") 
		wait_group.Done() 
	}() 
 
 
	wait_group.Wait() 
	//close(test53_1) 
	fmt.Println("所有的子协程都结束") 
 
} 

  

原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/20920.html

(0)
上一篇 2021年7月19日
下一篇 2021年7月19日

相关推荐

发表回复

登录后才能评论