首页 > 其他分享 >day8 golang-chan-协程-定时器-锁-等待组

day8 golang-chan-协程-定时器-锁-等待组

时间:2023-04-14 20:12:22浏览次数:47  
标签:协程 day8 chan sync golang c1

package main

import (
	"fmt"
	"math/rand"
	"sync"
	"sync/atomic"
	"time"
)

func example1() {
	//不要这样写,阻塞就死无法解除,零值nil
	var c1 chan int
	fmt.Printf("%d,%d,%v", len(c1), cap(c1), c1)
	//c1 <- 1  //阻塞不报错,由于没有初始化容器,1塞不进去,死锁。
	<-c1                  //也阻塞,什么都拿不出来,死锁
	fmt.Println("111111") //上面阻塞了死锁了 这个是不能打印出来的
}

func example2() {
	//通过make创建
	//非缓冲通道,容量为0,也叫同步通道。发送第一个元素时,如果没有接收操作就立即阻塞,知道接收,同样接收时,如果没有数据发送就立即阻塞,知道数据发送
	c2 := make(chan int, 0)
	c3 := make(chan int)
	fmt.Printf("c2: %d,%d,%v\n", len(c2), cap(c2), c2) //c2: 0,0,0xc0000103c0
	fmt.Printf("c3: %d,%d,%v\n", len(c3), cap(c3), c3) //c3: 0,0,0xc000010420

	//缓冲通道
	c4 := make(chan int, 8)
	fmt.Printf("c4: %d,%d,%v\n", len(c4), cap(c4), c4) //c4: 0,8,0xc0000240a0
	//往通道发送数据
	c4 <- 1
	c4 <- 2
	fmt.Printf("c4: %d,%d,%v\n", len(c4), cap(c4), c4) //c4: 2,8,0xc0000240a0
	//从通道拿数据接收数据
	<-c4                      //拿走,扔了
	t := <-c4                 //拿出来赋值给t
	fmt.Printf("%T,%[1]v", t) //int,2

}

//单项通道 chan<- type 只往一个chan里面写,<-chan type 只从chan里面拿
func produce(ch chan<- int) {
	for {
		ch <- rand.Intn(10)
		time.Sleep(time.Second)

		//关闭通道,只有发送方才能关闭,一旦关闭,在发送数据就panic,
		//如果去掉for,通道只有一个数据,关闭通道,接收者依然可以访问关闭的通道而不阻塞,
		//t,ok :=<-ch获取数据失败,ok为false,返回零值
		//close(ch) //如果再次关闭直接panic
	}
}
func consume(ch <-chan int) {
	for {
		t, ok := <-ch
		fmt.Println("包子被吃了,id:", t, ok)
	}
}
func example3() {
	//等待组
	var wg sync.WaitGroup
	wg.Add(1)
	fmt.Println("准备吃包子了")
	c := make(chan int)
	go produce(c)
	go consume(c)
	wg.Wait()
}
func example4() {
	//遍历通道
	//缓冲的关闭的通道
	c1 := make(chan int, 5)
	c1 <- 1
	c1 <- 2
	c1 <- 3
	close(c1)
	fmt.Println(<-c1, "故意从通道放走一个")
	for v := range c1 {
		fmt.Println(v)
	}
	fmt.Println("end,不关闭通道,你看不见我")
}
func example5() {
	//1、非缓冲的未关闭的通道
	//相当于一个无限元素的通道,迭代不完,阻塞在等下一个元素到达
	//2、非缓冲关闭的通道
	//关闭后,通道不能在进入新的元素,那么相当于遍历有限个元素容器,遍历完就结束了
	c1 := make(chan int) //非缓冲通道
	go func() {
		defer close(c1)
		count := 1
		for i := 0; i < 5; i++ {
			time.Sleep(3 * time.Second)
			c1 <- count
			count++
		}
	}()
	for v := range c1 {
		fmt.Println("v print:", v)
	}
	fmt.Println("不关闭通道,我就死锁,你就看不到我")
}
func example6() {
	//定时器
	go func() {
		t := time.NewTicker(2 * time.Second) //定义2秒的定时器
		for {
			fmt.Println("我是Ticker定时器阻塞2s的:", <-t.C) //通道阻塞住每隔2秒就接收一次
		}
	}()
	go func() {
		t := time.NewTimer(5 * time.Second)
		for {
			fmt.Println("Timer我开始了")
			fmt.Println("我是Timer定时器阻塞5s的:", <-t.C) //通道阻塞5s后只接收一次,再来就阻塞住
			fmt.Println("Timer我接收完了")
		}
		fmt.Println("Timer我接收完了,但我不会打印出来")
	}()
	time.Sleep(100 * time.Second)
}
func example7() {
	//通道死锁
	c1 := make(chan int)
	c1 <- 1 //当前协程阻塞。无人能解。死锁

}
func example8() {
	//struct{}型通道
	//如果一个结构体类型就是struct{},说明该结构体的实例没有数据成员,也就是实例内存占用为0,节约内存,仅仅是为了传递一个信号标志
	flag := make(chan struct{}) //比chan bool生内存
	go func() {
		time.Sleep(3 * time.Second)
		flag <- struct{}{} //无数据成员的结构体实例
	}()
	fmt.Printf("等到了信号,%T,%[1]v\n", flag)
}
func example9() {
	//通道多路复用
	count := make(chan int, 4)
	fin := make(chan struct{})
	go func() {
		defer func() {
			fin <- struct{}{}
		}()
		for i := 0; i < 10; i++ {
			count <- i
			time.Sleep(time.Second)
		}
	}()
	for {
		select {
		case n := <-count:
			fmt.Println("count:", n)
		case <-fin:
			fmt.Println("收到退出信号,跳出循环")
			goto END
		}
	}
END:
	fmt.Println("我跳出来了")
}
func inc(count *int64) {
	for i := 0; i < 100000; i++ {
		*count += 1
	}
}
func inc2(count *int64, wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		*count += 1
	}
}
func inc3(count *int64, wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		atomic.AddInt64(count, 1)
	}
}
func inc4(count *int64, mx *sync.Mutex, wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 0; i < 100000; i++ {
		mx.Lock()
		*count++
		mx.Unlock()
	}
}
func example10() {
	//通道并发 锁,加了锁会影响并行效率保证了逻辑正确
	var count int64 = 0
	start := time.Now()
	//串行没有并发的时候
	//inc(&count)
	//inc(&count)
	//inc(&count)
	//inc(&count)
	//inc(&count)
	// //fmt.Printf("Go协程数:%d\n", runtime.NumGoroutine())
	//fmt.Printf("执行了%dms,count is:%d\n", time.Since(start).Microseconds(), count) //执行了1590ms count正确

	//for i := 0; i < 5; i++ {
	//	go inc(&count)
	//	// //fmt.Printf("Go协程数:%d\n", runtime.NumGoroutine())
	//}
	//fmt.Printf("执行了%dms,count is:%d\n", time.Since(start).Microseconds(), count) //执行了0-541ms count不对

	//var wg sync.WaitGroup
	//wg.Add(5)
	//for i := 0; i < 5; i++ {
	//	go inc2(&count, &wg)
	//}
	////	fmt.Printf("Go协程数:%d\n", runtime.NumGoroutine())
	//wg.Wait()
	//fmt.Printf("执行了%dms,count is:%d\n", time.Since(start).Microseconds(), count) //执行了512ms,count is:14466

	////上面两个例子加了go协程后 最终得到结果完全不对,原因是count++不是原子操作,会被打断。1、原子操作 2、加锁保证结果正确

	//==================原子操作
	//var wg sync.WaitGroup
	//wg.Add(5)
	//for i := 0; i < 5; i++ {
	//	go func() {
	//		defer wg.Done()
	//		for i := 0; i < 100000; i++ {
	//			atomic.AddInt64(&count, 1)
	//		}
	//	}()
	//	//或者把上面的匿名函数扔出去,把count和wg传进去
	//	//go inc3(&count, &wg)
	//}
	//wg.Wait()
	//fmt.Printf("执行了%dms,count is:%d\n", time.Since(start).Microseconds(), count) //12705ms  count:500000

	//=======互斥锁
	var wg sync.WaitGroup
	var mx sync.Mutex
	wg.Add(5)
	for i := 0; i < 5; i++ {
		go inc4(&count, &mx, &wg)
	}
	//	fmt.Printf("Go协程数:%d\n", runtime.NumGoroutine())
	wg.Wait()
	fmt.Printf("执行了%dms,count is:%d\n", time.Since(start).Microseconds(), count) //执行了44866ms,count is:500000

}

func main() {
	//example1()
	//example2()
	//example3()
	//time.Sleep(100 * time.Second) //如果开了协程,主协程运行完程序就结束了,笨方法是sleep一会;第二个方法加等待组

	//example4()
	//example5()
	//example6()
	//example7()
	//example8()
	//example9()
	example10()
}

  

标签:协程,day8,chan,sync,golang,c1
From: https://www.cnblogs.com/dribs/p/17319807.html

相关文章

  • org.apache.flume.ChannelFullException: Space for commit to queue couldn't be acq
    做以下修改agent.channels.memoryChanne3.keep-alive=60agent.channels.memoryChanne3.capacity=1000000修改java最大内存大小vibin/flume-ngJAVA_OPTS="-Xmx2048m"参考http://fangjian0423.github.io/2016/01/19/flume-channel-full-exception/......
  • 面试某大厂,被Channel给吊打了,这次一次性通关channel!
    目录一前言面试题然后我们进行一下扩展,玩转Channel!二解决面试题1.介绍一下Channel2.Channel在go中起什么作用3.Channel为什么需要两个队列实现4.Go为什么要开发Channel,而别的语言为什么没有5.Channel底层是使用锁控制并发的,为什么不直接使用锁三扩展面试题1.Channel......
  • git 更新代码错误 Your local changes to the following files would be overwritten
    当gitpull时提示 Yourlocalchangestothefollowingfileswouldbeoverwrittenbymerge idea中撤销当前本地本次提交 ......
  • 重构之Divergent Change(发散式变化)&Shotgun Surgery (散弹式修改)
    重构之DivergentChange(发散式变化)&ShotgunSurgery(散弹式修改) 5.DivergentChange发散式变化描述:一个类被锚定了多个变化,当这些变化中的任意一个发生时,就必须对类进行修改。解释:一个类最好只因一种变化而被修改操作:你应该找出某特定原因而造成的所有变化,然后运用ExtractCl......
  • Android Kotlin实战之高阶使用泛型扩展协程懒加载详解
    前言:通过前面几篇文章,我们已基本掌握kotlin的基本写法与使用,但是在开发过程中,以及一些开源的API还是会出现大家模式的高阶玩法以及问题,如何避免,接下来讲解针对原来的文章进行一些扩展,解决大家在工作中遇到的问题,如何去解决如果还有人不了解kotlin,可以查看我的基础篇kotlin。Android......
  • git pull时,提示Your local changes to the following files would be overwritten by
    问题描述:本地修改了代码后,执行“gitpull”命令时,无法更新代码,并报错提示:“Yourlocalchangestothefollowingfileswouldbeoverwrittenbymerge” 问题原因:是因为本地修改的代码与git服务器的代码冲突导致。如果不冲突,会自动更新合并代码。 gitpull冲突的解决办......
  • 动力节点王鹤SpringBoot3笔记——第六章 远程访问@HttpExchange[SpringBoot 3]
    第六章 远程访问@HttpExchange[SpringBoot3]远程访问是开发的常用技术,一个应用能够访问其他应用的功能。SpringBoot提供了多种远程访问的技术。基于HTTP协议的远程访问是支付最广泛的。SpringBoot3提供了新的HTTP的访问能力,通过接口简化HTTP远程访问,类似Feign功能。Spring......
  • Linux change archive mirror All In One
    LinuxchangearchivemirrorAllInOneLinux/Debian/Ubuntu/RaspberryPiOS$sudoapt-getupdateHit:1http://deb.debian.org/debianbullseyeInReleaseGet:2http://security.debian.org/debian-securitybullseye-securityInRelease[48.4kB]Get:3ht......
  • 【学习笔记】go协程和通道
    虽然,线程池为逻辑编写者提供了线程分配的抽象机制。但是,如果面对随时随地可能发生的并发和线程处理需求,线程池就不是非常直观和方便了。能否有一种机制:使用者分配足够多的任务,系统能自动帮助使用者把任务分配到CPU上,让这些任务尽量并发运作。这种机制在Go语言中被称为goroutine。go......
  • 生成器、协程
    生成器、协程目录生成器、协程1协程和生成器2生成器Generator2.1列表生成式2.2生成器2.3斐波拉契数列(Fibonacci)2.3.1斐波拉契数列函数写法2.3.2yield方式生成斐波拉契数列函数2.4yield生成器返回值特点2.5yieldfrom3协程3.1协程介绍3.1.1存在yield函数运行过程3.1......