网站首页 > 技术文章 正文
接上一篇关于go语言中的协程相关知识点总结(二)继续分享 关于go协程关于select的知识点
select关键字
select可以监听处理多个channel,它不是按顺序执行的,它会对所有的channel都是同进检查的,如果select中的channel没有准备好,则会进行阻塞操作,如果有多个channel同时准备好,则会随机选择一个
举个例子
package main
import (
"fmt"
"math/rand"
"os"
"strconv"
"time"
)
func gen(min,max int ,createNum chan int,end chan bool) {
//使用for,是为了一直在监听
for {
//使用select 可以同时监听多个channel通道
select {
case createNum<-rand.Intn(max-min)+min:
case <-end:
close(end)
return
//这里的time.After()做了个超时处理
case <-time.After(4*time.Second):
fmt.Println("time.after")
return
}
}
}
func main() {
rand.Seed(time.Now().Unix())
createNum :=make(chan int)
end := make(chan bool)
if len(os.Args)!=2{
fmt.Println("参数异常")
return
}
n,_ :=strconv.Atoi(os.Args[1])
fmt.Println("创建的随机数是",n)
go gen(0,2*n,createNum,end)
for i := 0; i < 20; i++ {
fmt.Println("取值",<-createNum)
}
//注意下这里,由于取值完后,会等待5秒,但是time.After()只等待4秒,就会处理超时,退出等待,
time.Sleep(5*time.Second)
fmt.Println("退出")
//end<-true
}
利用time.After()来检查goroutine超时的两种方式
方式一:
package main
import (
"fmt"
"time"
)
func main() {
c := make(chan string)
go func() {
//这里主要是模拟一下业务操作用时
time.Sleep(time.Second*3)
c <-"c1 ok"
}()
select {
case res :=<-c:
fmt.Println("取值",res)
case <-time.After(time.Second*1):
fmt.Println("timout c1")
}
}
方式二:
这里只不过,超时时间可以通过参数可以控制
package main
import (
"fmt"
"os"
"strconv"
"sync"
"time"
)
func timeout(w *sync.WaitGroup,t time.Duration) bool{
temp :=make(chan int)
go func() {
time.Sleep(time.Second*5)
defer close(temp)
w.Wait()
}()
select {
case <-temp:
return false
case <-time.After(t):
return true
}
}
func main() {
arg := os.Args
if len(arg) !=2{
fmt.Println("参数缺失")
return
}
var w sync.WaitGroup
w.Add(1)
t,err :=strconv.Atoi(arg[1])
if err != nil {
fmt.Println(err)
return
}
dur := time.Duration(int32(t)) * time.Millisecond
fmt.Println("time时间",dur)
if timeout(&w,dur){
fmt.Println("timeout ")
}else{
fmt.Println("ok")
}
w.Done()
}
关于通道的知识点总结
- channel类型的零值是nil,如果给一个已关闭的channel发送消息,程序会崩溃,如果从一个一个已关闭的channel读取数据,会得到channel类型的 零值
- 一个nil的channel总是阻塞的,这个特性有时会用到
- 可缓冲通道
make(chan int ,5)//指定第二个参数,来说明可缓冲大小
通过共享变量来共享内存
当多个goroutine在任何给定时间访问共享变量,会出现数据竞争问题,这也是并发程序中需要考虑的,一个可以通过channel通道解决,也可以使用共享变量加锁实现,下面说说通过共享变量加锁的相关知识点
sync.Mutex类型
sync.Mmutex类型是Go实现的一个互斥体,可以使用sync.Lock()和sync.Unlock()来实现加锁和解锁,这两个要同时出现,如果不一致,会导致程序出现死锁
来个例子
package main
import (
"fmt"
"os"
"strconv"
"sync"
"time"
)
var(
m sync.Mutex
v1 int
w sync.WaitGroup
)
func change(i int) {
//加锁
m.Lock()
time.Sleep(time.Second)
v1=v1+1
if v1%10==0{
v1=v1-10*i
}
//解锁
m.Unlock()
}
func read()int {
m.Lock()
a := v1
m.Unlock()
return a
}
func main() {
if len(os.Args) !=2{
fmt.Println("参数缺失")
return
}
num,err :=strconv.Atoi(os.Args[1])
if err != nil {
fmt.Println(err)
return
}
fmt.Println(read())
for i := 0; i < num; i++ {
w.Add(1)
go func() {
defer w.Done()
change(i)
fmt.Println("->",read())
}()
}
w.Wait()
fmt.Println("-->",read())
}
sync.RWMutex类型
sync.RWMutex可以说是sync.Mutex的改进版,它只允许一个函数执行写操作,但可以同时允许有sync.RWMutex互斥锁的多个读取者,但是写操作就会阻塞,它有RLock()和RUnlock()是用于读操作的函数,Lock()和Unlock()用于写操作的函数
来个例子
package main
import (
"fmt"
"os"
"sync"
"time"
)
var password=secret{password: "12345"}
type secret struct{
RWM sync.RWMutex
M sync.Mutex
password string
}
func Change(c *secret,pass string) {
c.RWM.Lock()
fmt.Println("lchange")
time.Sleep(time.Second*10)
c.password=pass
c.RWM.Unlock()
}
func Show(c *secret)string {
//在读取的时候加了锁,这样,就会出现在读的时候,写操作是阻塞的,保证读出来的数据是一样的
c.RWM.RLock()
fmt.Println("show")
time.Sleep(time.Second*3)
defer c.RWM.RUnlock()
return c.password
}
func showWithLock(c *secret) string {
//由于这里使用了排他锁,所以同一时间只有一个函数可以读到值
c.M.Lock()
fmt.Println("showwithlock")
time.Sleep(time.Second*3)
defer c.M.Unlock()
return c.password
}
func main() {
var showFunc= func(c *secret) string{ return ""}
if len(os.Args)!=2{
fmt.Println("使用了sync.RWMutex")
showFunc=Show
}else{
fmt.Println("使用了sync.Mutex")
showFunc=showWithLock
}
var w sync.WaitGroup
fmt.Println(showFunc(&password))
for i := 0; i < 15; i++ {
w.Add(1)
go func() {
defer w.Done()
fmt.Println("go pass",showFunc(&password))
}()
go func() {
w.Add(1)
defer w.Done()
Change(&password,"12345")
}()
w.Wait()
fmt.Println("pass",showFunc(&password))
}
}
这次主要是总结了一下关于协程间通信和数据访问竞态的知识点
下一篇主要说一下
<<如果优雅的让协程退出>>
关注我,不迷路!!!!
猜你喜欢
- 2024-10-12 漫画 | Linux 并发和竞态问题究竟是什么?
- 2024-10-12 【驱动】串口驱动分析(三)-serial driver
- 2024-10-12 synchronized锁 synchronized锁的是类还是对象
- 2024-10-12 Golang 程序遇到性能问题该怎么办?
- 2024-10-12 线程间通信——互斥锁 线程间互斥方式
- 2024-10-12 【Linux系统编程】互斥锁 linux 互斥锁优先级反转
- 2024-10-12 linux c/c++开发:多线程并发锁:互斥锁、自旋锁、原子操作、CAS
- 2024-10-12 每行代码都带注释,带你看懂Go互斥锁的源码
- 2024-10-12 一文搞懂pprof 一文搞懂伤寒论六经辨证
- 2024-10-12 并发原理系列八:信号量、互斥锁、自旋锁
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)