Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

并发趣题-H2O制造工厂 #33

Open
kevinyan815 opened this issue Dec 15, 2020 · 0 comments
Open

并发趣题-H2O制造工厂 #33

kevinyan815 opened this issue Dec 15, 2020 · 0 comments

Comments

@kevinyan815
Copy link
Owner

kevinyan815 commented Dec 15, 2020

题目是这样的:

有一个名叫大自然的搬运工的工厂,生产一种叫做一氧化二氢的神秘液体。这种液体的分子是由一个氧原子和两个氢原子组成的,也就是水。

这个工厂有多条生产线,每条生产线负责生产氧原子或者是氢原子,每条生产线由一个 goroutine 负责。

这些生产线会通过一个栅栏,只有一个氧原子生产线和两个氢原子生产线都准备好,才能生成出一个水分子,否则所有的生产线都会处于等待状态。也就是说,一个水分子必须由三个不同的生产线提供原子,而且水分子是一个一个按照顺序产生的,每生产一个水分子,就会打印出 HHO、HOH、OHH 三种形式的其中一种。HHH、OOH、OHO、HOO、OOO 都是不允许的。

生产线中氢原子的生产线为 2N 条,氧原子的生产线为 N 条。

实现思路

首先,我们来定义一个 H2O 辅助数据类型,它包含两个信号量的字段和一个循环栅栏。

  1. semaH 信号量:控制氢原子。一个水分子需要两个氢原子,所以,氢原子的空槽数设置为 2。
  2. semaO 信号量:控制氧原子。一个水分子需要一个氧原子,所以资源数的空槽数设置为 1。
  3. 循环栅栏:等待两个氢原子和一个氧原子填补空槽,直到任务完成。
package main

import (
  "context"
  "github.com/marusama/cyclicbarrier"
  "golang.org/x/sync/semaphore"
)

// 定义水分子合成的辅助数据结构
type H2O struct {
  semaH *semaphore.Weighted // 氢原子的信号量
  semaO *semaphore.Weighted // 氧原子的信号量
  b     cyclicbarrier.CyclicBarrier // 循环栅栏,用来控制合成
}
func New() *H2O {
  return &H2O{
    semaH: semaphore.NewWeighted(2), //氢原子需要两个
    semaO: semaphore.NewWeighted(1), // 氧原子需要一个
    b:     cyclicbarrier.New(3),  // 需要三个原子才能合成
  }
}

接下来,我们看看各条流水线的处理情况。

流水线分为氢原子处理流水线和氧原子处理流水线,首先,我们先看一下氢原子的流水线:如果有可用的空槽,氢原子的流水线的处理方法是 hydrogenhydrogen方法会占用一个空槽(h2o.semaH.Acquire),输出一个 H 字符,然后等待栅栏放行。等其它的 goroutine 填补了氢原子的另一个空槽和氧原子的空槽之后,程序才可以继续进行。

func (h2o *H2O) hydrogen(releaseHydrogen func()) {
  h2o.semaH.Acquire(context.Background(), 1)

  releaseHydrogen() // 输出H
  h2o.b.Await(context.Background()) //等待栅栏放行
  h2o.semaH.Release(1) // 释放氢原子空槽
}

然后是氧原子的流水线。氧原子的流水线处理方法是oxygenoxygen 方法是等待氧原子的空槽,然后输出一个 O,就等待栅栏放行。放行后,释放氧原子空槽位。

func (h2o *H2O) oxygen(releaseOxygen func()) {
  h2o.semaO.Acquire(context.Background(), 1)

  releaseOxygen() // 输出O
  h2o.b.Await(context.Background()) //等待栅栏放行
  h2o.semaO.Release(1) // 释放氢原子空槽
}

在栅栏放行之前,只有两个氢原子的空槽位和一个氧原子的空槽位。只有等栅栏放行之后,这些空槽位才会被释放。栅栏放行,就意味着一个水分子组成成功,流水线才能继续运输和面的H和O原子。

两条流水线上执行的输送原子操作可以定义如下

// 用来存放水分子结果的channel
var ch chan string

// 往channel里存放一个H原子
func releaseHydrogen() {
	ch <- "H"
}

// 往channel里存放一个O原子
func releaseOxygen() {
	ch <- "O"
}

channel的另一端,就能以两个H原子一个O原子的组合不断接受到流水线运送过来的原子,组合成H2O水分子。

查看完整源代码

@kevinyan815 kevinyan815 changed the title 并发趣题-H2O制造厂 并发趣题-H2O制造工厂 Dec 15, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant