Go 并发可视化解释 – Semaphore

在这个系列的最后两篇文章中,我们讨论了来自sync包的MutexRWMutex。当我们希望只有一个Goroutine能够独占地访问共享数据时,这两个结构非常有用。

然而,在现实生活中,有些用例需要允许多个用户同时访问共享资源。这个数量可以很大,也可以很小,但必须始终是有限的。例如,一个容纳60000人的体育场在任何时候都不应容纳超过这个数量的人。或者,在机场,无论乘客队列有多长,同时允许办理登机手续的最大乘客数量就是开放柜台的数量。在计算机科学中,这种并发访问的用例是用信号量(Semaphore)来建模的。在本文中,我将通过可视化的方式解释信号量(Semaphore)的工作原理。我还将与您分享如何在Golang中使用通道来简单实现信号量(Semaphore)的方法。

场景

有4个Gopher想去游泳。然而,只有2个游泳道。每个泳道最多允许1个Gopher在任何时候游泳,但他们可以轮流游泳。

Candier想去游泳。因为两个泳道都可用,所以她可以立即获得一个泳道。

1*rNpdTpCmhFyRV0FULxkBvQ.png

片刻之后,Partier和Swimmer也想去游泳。此时只有一个泳道可用。只有其中一个可以获得泳道,另一个必须等待。当M个Goroutine竞争N(N < M)个槽位时,我们不能保证谁会赢得这个“竞争”。在这种情况下,当M=2N=1时,假设Swimmer赢了。

Go 并发可视化解释 – Semaphore
1*hMzIXmAgnnqLXUhD8LTIOQ.png

Swimmer非常擅长这项运动。他迅速完成了他的轮次,并迅速释放了泳道,使其可用于Partier。与此同时,Candier仍然在她的泳道上游泳。

Go 并发可视化解释 – Semaphore
1*wtiF0BMWk8KY144wVXSgOg.png

Stringer想去游泳,但两个泳道目前都被占用。他别无选择,只能等待。他不知道也不关心哪一条泳道会先可用。

Go 并发可视化解释 – Semaphore
1*jxskbQzVSTQLLp6Lo3nGnQ.png

假设Partier在这项运动中也比Candier更有天赋。尽管在Candier之后开始,但仍然比她早完成。Partier释放了他的泳道,使其可用于Stringer。

1*kiRY3yaZ1GUjxu75DLGP5g.png

不久后,Candier完成并释放了她的泳道。泳道变得可用,但没有人试图占用它,它仍然可用。

最后,Stringer完成了他的轮次。他释放了他的泳道,使两个泳道都可用。

1*Xc6fPx1gWLDIQYLv_yL9ww.png

面试

在技术面试中,Semaphore这个术语可能听起来有点吓人。然而,正如你在上面的插图中所看到的,它是非常容易理解的。事实上,我曾几次在面试中问过我的面试者关于Semaphore的问题。例如,设计一个在黑色星期五上线的虚拟商店。有很多顾客想进去,但商店最多只能容纳N个顾客。每个顾客进去后没有时间限制,他/她可以一整天待在店里,也可以一进去就离开。当已经有N个顾客在里面时,后来的顾客必须排队等候,直到有人离开。当然,有很多解决这个问题的方法,其中没有一个是对或错的。我希望这篇文章为您提供了处理N个并发访问问题的另一种工具。

真实世界的例子

办理登机手续柜台、体育场的座位以及计算机资源,如CPU、内存和网络,有一个共同点:它们都是有限的。通常,控制应用程序资源使用是一个好主意。我曾在我的一个应用程序中使用Semaphore来限制一次只能有限数量的并发资源密集型Goroutine。这也可以通过一个包含N个Goroutine的池来解决。然而,由于我们的并发工作负载不是均匀分布在时间上的,它可能在0和N之间的任何位置,所以我们发现Semaphore是一个更好的选择。

展示你的代码!

请注意,Semaphore并不像sync.Mutex一样作为内置组件提供。相反,Go团队将其作为扩展提供。将其添加到项目中非常简单:go get golang.org/x/sync

package main
import ( \\\"context\\\" \\\"golang.org/x/sync/semaphore\\\" \\\"log\\\" \\\"time\\\")
func main() { pool := semaphore.NewWeighted(2)
go swim(\\\"Candier\\\", pool) go swim(\\\"Swimmer\\\", pool) go swim(\\\"Partier\\\", pool) go swim(\\\"Stringer\\\", pool)
time.Sleep(5 * time.Second) // For brevity, better use sync.WaitGroup
log.Println(\\\"Main: Done, shutting down\\\")}
func swim(name string, pool *semaphore.Weighted) { log.Printf(\\\"%v: I want to swim\\\\n\\\", name)
// In real applications, pass in your context such as HTTP request context ctx := context.Background()
// We can also Acquire/Release more than 1 // when the workloads consume different amount of resources if err := pool.Acquire(ctx, 1); err != nil { log.Printf(\\\"%v: Ops, something went wrong! I cannot acquire a lane\\\\n\\\", name) return }
log.Printf(\\\"%v: I got a lane, I\\\'m swimming\\\\n\\\", name) time.Sleep(time.Second) log.Printf(\\\"%v: I\\\'m done. Releasing my lane\\\\n\\\", name) pool.Release(1)}

实现自己的Semaphore

如果您不想将golang.org/x/sync/semaphore添加到项目中,使用通道自己实现Semaphore也相当简单。

type Semaphore struct {    ch chan bool}
func NewSemaphore(weight int) *Semaphore { return &Semaphore{ ch: make(chan bool, weight), }}
func (s *Semaphore) Acquire() { s.ch <- true}
func (s *Semaphore) Release() { <-s.ch}}

原创文章,作者:小技术君,如若转载,请注明出处:https://www.sudun.com/ask/33956.html

(0)
小技术君的头像小技术君
上一篇 2024年4月14日
下一篇 2024年4月14日

相关推荐

  • 中国十大cdn公司,中国十大cdn公司如何收费的

    标题:中国十大CDN公司:为您揭示网络加速的先锋! 网络加速行业在中国蓬勃发展,CDN(内容分发网络)服务在这一领域扮演着至关重要的角色。本文将为您介绍中国十大CDN公司,深入探讨…

    2024年5月11日
    0
  • cdn自定义规则与策略

    CDN(内容分发网络)通常提供了自定义规则与策略的功能,让用户可以根据自己的需求和情况对流量进行更精细化的控制和管理。以下是一些常见的CDN自定义规则与策略: 这些自定义规则与策略…

    CDN资讯 2024年2月10日
    0
  • cdn设备,cdn设备属于什么ce类型

    CDN设备,一提到这个话题,无疑是当今网络世界中的热点之一。随着互联网的迅猛发展,网站的访问量与数据传输需求不断增长,传统的服务器架构已经无法满足用户对高速、稳定访问的需求。在这样…

    2024年5月11日
    0
  • DNS的工作原理

      DNS(Domain Name System,域名系统)是一种分布式互联网命名系统,主要用于将人们易于记忆的域名转换成计算机用于识别的IP地址(如www.exampl…

    2024年3月24日
    0

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注