- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
译自: Designing for Concurrency: the Hilbert’s Hotel Problem in Go ,本文使用go的并发性来解决Hilbert酒店问题。本文比较有意思的是它对问题的描述很吸引人,在看完文字描述之后,代码实现逻辑也基本顺理成章,当然代码本身的实现也相当优雅.
文章一开始叙述了并发和并行的区别和联系,此处略去该部分.
Hilbert酒店 是一个与无限有关的问题.
假设Hilbert是一个酒店的所有者,且该酒店有无限个房间.
一天一个大巴达到了Hilbert的酒店,假设大巴上有无限个旅客想要住在Hilbert的酒店,假设Hilbert的酒店有无限个房间,因此能够容纳无限个旅客.
但有一天有无限辆大巴达到了Hilbert的酒店,每辆大巴上载有无限个旅客,且所有大巴的旅客都要在酒店过夜.
Hilbert想了一会说"没问题",然后他画了一个数字三角形,然后把房间的钥匙递给沿着三角形对角线的所有乘客。通过这种方式,Hilbert能够给每辆大巴的每个乘客一把唯一的钥匙.
我们可以通过重复Hilbert的做法来解决Hilbert酒店问题.
例如,可以创建数组的数组来代表三角形的数字,然后遍历该数组的数组来构建对角线并沿着对角线将房间钥匙交给每辆大巴的乘客。文章末给出了这种方式的实现,当然也存在其他的非并发实现。下面换个角度看下这个问题.
递归并发算法中,每辆大巴所需的钥匙位于三角形的最右侧对角线。对角线中的钥匙所在的位置等于三角形高度,这一点就是判定某把钥匙是不是本大巴的关键.
假设Hilbert的酒店中有无限个雇员,每个雇员负责给一辆大巴的所有旅客发放钥匙。负责第一辆大巴的雇员(Bus 1 Clerk)靠近负责第二辆大巴的雇员(Bus 2 Clerk),负责第二辆大巴的雇员(Bus 2 Clerk)靠近负责第三辆大巴的雇员(Bus 3 Clerk),以此类推.
此外还有一个雇员(Room Key Clerk),他负责将所有房间的钥匙依次交给第一个雇员(Bus 1 Clerk),即房间一是第一把钥匙,房间二是第二把钥匙,房间三是第三把钥匙,以此类推.
(从Room Key Clerk接收到钥匙的)Bus 1 Clerk知道接收到的第一把钥匙是给他负责的大巴的第一个乘客,因此Bus 1 Clerk会为第一辆巴士的第一位乘客准备一号房间的含钥匙在内的欢迎礼包(welcome kit),此外他还知道接收到的第二把钥匙不是给他负责的大巴的,而是给下一个雇员的,第三把钥匙是给Bus 1的,因此由Bus 1 Clerk负责,但第四把和第五把钥匙不是给Bus 1的,因此Bus 1 Clerk会将它们传给下一个雇员。当欢迎礼包发放给Bus 1的所有乘客之后(此时需要假设顾客是有限的,防止程序无法结束),Bus 1 Clerk会将它们返还给Hilbert。后面将会看到,将钥匙还给Hilbert是一个有用的步骤,可以并发实现最终的类似reduce的操作.
下一个雇员Bus 2 Clerk的行为和第一个雇员相同,他知道接收到的第一个钥匙需要交给他负责的大巴的第一个乘客,而第二把钥匙则需要交给下一个雇员,以此类推.
在某种程度上,由于我们的程序不能像原来的Hilbert问题那样永远继续下去,因此需要能够停止移交钥匙,并通知所有雇员停止工作。此时需要将雇员准备的欢迎礼包还给Hilbert。最终,所有的雇员都会停止工作,并在Hilbert接收到准备好的欢迎礼包之后就可以通知Hilbert也停止工作.
这里提供了一个Go实现的 并发算法 。使用goroutine来代表每一个角色,包括Hilbert和雇员.
下面是Hilbert的实现:
func Hilbert(upTo int) {
keysCh := make(chan int)
go RoomKeysClerk(upTo, keysCh) //1
make(chan []hilberthotel.WelcomeKit)
go BusClerk(1, keysCh, welcomeKitCh) //2
for envelope := range welcomeKitCh { //3
fmt.Println(envelope)
}
}
其代码比较简单,入参 upTo 表示房间钥匙的上限:
keysch
,并使用 keysch
作为参数来启动Room Key Clerk的goroutine。 welcomeKitCh
(雇员会从该channel中接收钥匙,并在雇员结束工作后发送欢迎礼包),用于接收Welcome Kits(欢迎礼包),并使用 keysch
和 welcomeKitCh
作为参数来启动第一个BusClerk(大巴雇员) welcomeKitCh
循环接收雇员准备的礼包 Room Key Clerk 的实现也很简单,它通过 keysCh 将钥匙分发出去,在钥匙到达上限 upTo 之后,关闭 keysCh :
func RoomKeysClerk(upTo int, keysCh chan<- int) {
for i := 0; i < upTo; i++ {
keysCh <- i + 1
}
close(keysCh)
}
Bus Clert的实现要复杂一些:
func BusClerk(busNumber int, roomKeysCh <-chan int, welcomeKitsCh chan<- []hilberthotel.WelcomeKit, buffer int, delay time.Duration) { //1
var count = 0 //2
var keyPosition = 0
var nextClerkCh chan int
welcomeKits := []hilberthotel.WelcomeKit{}
for roomKey := range roomKeysCh {
if nextClerkCh == nil { //3
nextClerkCh = make(chan int, buffer)
go BusClerk(busNumber+1, nextClerkCh, welcomeKitsCh, buffer, delay)
}
if count == keyPosition { //4
kit := hilberthotel.NewWelcomeKit(busNumber, keyPosition, roomKey, delay)
welcomeKits = append(welcomeKits, kit)
keyPosition++
count = 0
continue
}
nextClerkCh <- roomKey
count++
}
if nextClerkCh != nil { //5
welcomeKitsCh <- welcomeKits
close(nextClerkCh)
} else {
close(welcomeKitsCh) //6
}
}
welcomeKitCh
用于在处理结束之后发送欢迎礼包( welcomeKits
), roomKeysCh
用于读取钥匙号 count
之后,使用一个变量 keyPosition
来保存下一个乘客的钥匙位置,使用channel nextClerkCh
通知下一个BusClerk。通过循环读取 roomKeysCh
来启动整个处理逻辑。 nextClerkCh
为nil,之后会初始化 nextClerkCh
并启动下一个BusClerk count
和 keyPosition
,用于确定此时的钥匙是给本大巴的还是给其他大巴的。当钥匙是给本大巴的,它会创建一个WelcomeKit并将其保存在它的集合中,反之,将钥匙传给下一个BusClerk roomKeysCh
被关闭),它会关闭用于通知下一个BusClerk的 nextClerkCh
channel welcomeKitCh
并通知Hilbert其将结束任务 上述解决方案基于"相对独立"的处理流程:
相对独立
于Bus Clerk 1,这里说的"相对独立"并不是完全独立,因为Room Key Clerk还需要等待Bus Clerk 1从 keyCh
channel接收钥匙(即使用了带缓存的 keysCh channel,缓冲区仍然会被填满,从而迫使 Room Key Clerk 等待Bus Clerk 1从通道读取数据) welcomeKitCh
channel中接收welcomeKits 因此,我们可以说所有的Bus Clerk和Hilber执行的都是"相对独立"的逻辑,需要通过调整channel的发送/接收来达到期望的结果.
虽然我们的并发设计实现的方案很优雅,但它也带来了如下开销:
另一方面,这种设计的好处在于,如果在多核硬件上运行该并发算法,算法中固有的并行逻辑会带来性能上的提升.
每个大巴雇员都有一些核心的工作需要完成,此外还有一些并发编排的工作(即配置goroutine和通过channel发送/接收钥匙).
并行可以提升核心工作的性能。在Hilbert的例子中,核心工作就是为每个客户准备欢迎礼包(即函数NewWelcomeKit执行的内容)。能够并行执行的核心工作越多,就越能在相同的时间内服务更多的顾客.
为了实现并行处理,我们需要执行一些并发编排工作。与并发编排工作相比,核心工作占主导地位越高,从并行性中获得的好处就越多。在Hilbert的例子中,每个大巴雇员的核心工作是准备欢迎礼包。因此,准备欢迎礼包的工作占比越高,多核硬件上运行并发设计的效率就越高。另一方面,处理的顾客越多,并发编排工作也就越重(由于需要在更多的goroutines之间进行切换和发送/接收钥匙),因此并发的成本也会越高.
可以使用 样例代码 提供的 benchmarks ,通过变更顾客数目来对性能进行验证.
也可以使用上面所使用的并发方案的基本设计导出 非并发递归 方案:
最后此篇关于使用go的并发性来解决Hilbert酒店问题的文章就讲到这里了,如果你想了解更多关于使用go的并发性来解决Hilbert酒店问题的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我在网上搜索但没有找到任何合适的文章解释如何使用 javascript 使用 WCF 服务,尤其是 WebScriptEndpoint。 任何人都可以对此给出任何指导吗? 谢谢 最佳答案 这是一篇关于
我正在编写一个将运行 Linux 命令的 C 程序,例如: cat/etc/passwd | grep 列表 |剪切-c 1-5 我没有任何结果 *这里 parent 等待第一个 child (chi
所以我正在尝试处理文件上传,然后将该文件作为二进制文件存储到数据库中。在我存储它之后,我尝试在给定的 URL 上提供文件。我似乎找不到适合这里的方法。我需要使用数据库,因为我使用 Google 应用引
我正在尝试制作一个宏,将下面的公式添加到单元格中,然后将其拖到整个列中并在 H 列中复制相同的公式 我想在 F 和 H 列中输入公式的数据 Range("F1").formula = "=IF(ISE
问题类似于this one ,但我想使用 OperatorPrecedenceParser 解析带有函数应用程序的表达式在 FParsec . 这是我的 AST: type Expression =
我想通过使用 sequelize 和 node.js 将这个查询更改为代码取决于在哪里 select COUNT(gender) as genderCount from customers where
我正在使用GNU bash,版本5.0.3(1)-发行版(x86_64-pc-linux-gnu),我想知道为什么简单的赋值语句会出现语法错误: #/bin/bash var1=/tmp
这里,为什么我的代码在 IE 中不起作用。我的代码适用于所有浏览器。没有问题。但是当我在 IE 上运行我的项目时,它发现错误。 而且我的 jquery 类和 insertadjacentHTMl 也不
我正在尝试更改标签的innerHTML。我无权访问该表单,因此无法编辑 HTML。标签具有的唯一标识符是“for”属性。 这是输入和标签的结构:
我有一个页面,我可以在其中返回用户帖子,可以使用一些 jquery 代码对这些帖子进行即时评论,在发布新评论后,我在帖子下插入新评论以及删除 按钮。问题是 Delete 按钮在新插入的元素上不起作用,
我有一个大约有 20 列的“管道分隔”文件。我只想使用 sha1sum 散列第一列,它是一个数字,如帐号,并按原样返回其余列。 使用 awk 或 sed 执行此操作的最佳方法是什么? Accounti
我需要将以下内容插入到我的表中...我的用户表有五列 id、用户名、密码、名称、条目。 (我还没有提交任何东西到条目中,我稍后会使用 php 来做)但由于某种原因我不断收到这个错误:#1054 - U
所以我试图有一个输入字段,我可以在其中输入任何字符,但然后将输入的值小写,删除任何非字母数字字符,留下“。”而不是空格。 例如,如果我输入: 地球的 70% 是水,-!*#$^^ & 30% 土地 输
我正在尝试做一些我认为非常简单的事情,但出于某种原因我没有得到想要的结果?我是 javascript 的新手,但对 java 有经验,所以我相信我没有使用某种正确的规则。 这是一个获取输入值、检查选择
我想使用 angularjs 从 mysql 数据库加载数据。 这就是应用程序的工作原理;用户登录,他们的用户名存储在 cookie 中。该用户名显示在主页上 我想获取这个值并通过 angularjs
我正在使用 autoLayout,我想在 UITableViewCell 上放置一个 UIlabel,它应该始终位于单元格的右侧和右侧的中心。 这就是我想要实现的目标 所以在这里你可以看到我正在谈论的
我需要与 MySql 等效的 elasticsearch 查询。我的 sql 查询: SELECT DISTINCT t.product_id AS id FROM tbl_sup_price t
我正在实现代码以使用 JSON。 func setup() { if let flickrURL = NSURL(string: "https://api.flickr.com/
我尝试使用for循环声明变量,然后测试cols和rols是否相同。如果是,它将运行递归函数。但是,我在 javascript 中执行 do 时遇到问题。有人可以帮忙吗? 现在,在比较 col.1 和
我举了一个我正在处理的问题的简短示例。 HTML代码: 1 2 3 CSS 代码: .BB a:hover{ color: #000; } .BB > li:after {
我是一名优秀的程序员,十分优秀!