golang 并发一

confucianism · · 868 次点击 · 开始浏览    置顶
这是一个创建于 的主题,其中的信息可能已经有所发展或是发生改变。

# golang并发一 在go里,每一个并发执行的活动称为goroutine。go有两种并发编程风格,通信顺序进程模式(基于channel),共享内存。 在go里面创建一个goroutine特别简单,只需在函数前面添加关键字go即可 ```go func f() { //TODO ADD CODE HERE } func main() { go f() } ``` go f()即创建了一个goroutine,并且并发执行函数f ## 通信顺序进程模式 ### 通道 通道是goroutine之间的连接,可以让一个goroutine将值发送给另一个goroutine。每个通道都有一个具体的类型,一个int型的通道写作chan int。 通道有三个操作:发送,接收,关闭(不常用)。 缓冲通道有一个缓冲队列,接收操作从队列的头部移除一个元素,发送操作在队列的尾部插入一个元素,如果队列已满,则发送操作阻塞,队列为空,则接受操作为空。 当通道容量为0时,发送操作会阻塞,直到另一个goroutine进行接收操作,这时值传送完成,两个goroutine继续工作。接收操作同理。 代码示例 - 创建通道 ```go channel := make(chan int) ``` - 创建缓冲通道 ```go channel := make(chan struct{},3) ``` - 向通道发送数据 ```go channel <- 1 ``` - 从通道接收数据 ```go _ = <- channel ``` - 关闭通道 ```go close(channel) ``` 如果一个通道关闭之后,任何发送操作都会引起宕机,接收操作马上返回通道的元素类型的0值,不阻塞 我们可能需要限制某个函数只可以进行发送操作,某个函数只可以进行接收操作,这可以通过在参数类型中声明chan <- int 表示这可以进行发送操作,类型<- chan int表示只可以进行接收操作。 一下代码我们实现一个生产者和消费者示例: ```go func main() { channel := make(chan int, 10) for i := 0; i < 10; i++ { go procedur(channel) } for i := 0; i < 12; i++ { go consumer(channel, i) } for { time.Sleep(time.Second) } } //生产者 只可以进行发送操作 func procedur(channel chan<- int) { for i := 0; i < 101; i++ { channel <- i i %= 100 time.Sleep(time.Millisecond * 100) } } //生产者 只可以进行接收操作 func consumer(channel <-chan int, id int) { for { v := <-channel fmt.Println(id, v) time.Sleep(time.Millisecond * 200) } } ``` 模仿了十个生产者和十二个消费者的情况 ### select 多路复用 select 语句类似switch 语句,有一系列情况和一个可选的默认分支,每一个情况指定一次通信和关联的代码块。 以下势力为随机生成数字以及输出的示例 ```go channel := make(chan int, 10) for i := 0; i < 100; i++ { select { case v := <-channel: fmt.Println(v) case channel <- i: } } ``` 需要注意的是,如果有多个情况同时满足,select随机选择一个进行执行。 ## 共享内存 ### 互斥锁 sync.Mutex 互斥锁模式应用非常广泛,所以sync包有一个单独的Mutex类型来支持这种模式。它的Lock方法用于获取锁,Unlock方法用于释放锁。如果正在获取的锁被已经被其他goroutine获取到,那么操作会一直阻塞,知道其他gorutine释放锁。所以一个共享变量可以通过一个互斥锁来进行保护。需要注意的是go语言的互斥量是不可再入的,即当前goroutine获取到锁后,不可再次获取该锁。 下面实现一个简单的账户余额充值提现代码。 ```go var ( balance float64 = 0 mutex = sync.Mutex{} ) //充值 func Recharge(value float64) { mutex.Lock() defer mutex.Unlock() time.Sleep(time.Millisecond * 10) balance += value } //提现 func WithDraw(value float64) bool { mutex.Lock() defer mutex.Unlock() time.Sleep(time.Millisecond * 10) //余额小于需要提现的金额,不可以提现 if balance < value { return false } balance -= value return true } //查看余额 func Balance()float64 { mutex.Lock() defer mutex.Unlock() return balance } ``` 充值和提现加入Sleep功能是为了增加协程在执行中对资源的抢占。 ### 读写互斥锁sync.RWMutex 在特殊环境下,我们可能需要读操作可以并发的执行,但是同一时间只可以有一个写操作,我们通过sync.RWMutex来实现。 ```go var ( balance float64 = 0 mutex = sync.RWMutex{} ) //查看余额 func Balance() float64 { mutex.RLock() defer mutex.RUnlock() return balance } ``` 通过RLock和RUnlock分别获取和释放一个度锁。RLock只能用在临界区域内对共享变量无写操作的情形。

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

868 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传