手撕 Goroutine 同步问题

1114. Print in Order 按序列印

The same instance of Foo will be passed to three different threads. Thread A will call first(), thread B will call second(), and thread C will call third(). Design a mechanism and modify the program to ensure that second() is executed after first(), and third() is executed after second().

本题要求阻塞一众并发的 Goroutine,然后按 One Two Three 的逻辑顺序列印结果

package goroutine

import "fmt"

var chFir = make(chan int)
var chSec = make(chan int)
var chThi = make(chan int)

func printFir() {
    fmt.Print("first")
    chFir <- 1
}

func printSec() {
    <-chFir
    fmt.Print("second")
    chSec <- 2
}

func printThi() {
    <-chSec
    fmt.Print("third")
    chThi <- 3
}

func PrintABC(arr []int) {  
    // arr := []int{1,2,3}
    funcList := map[int]func(){1: printFir, 2: printSec, 3: printThi}
    for _, v := range arr {
        go funcList[v]()
    }
    <-chThi
}

作者:li-er-dan-w
链接:https://leetcode-cn.com/problems/print-in-order/solution/golangxie-cheng-shi-xian-by-li-er-dan-w-kczv/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
PrintABC

1117. H2O / 分子合成

There are two kinds of threads, oxygen and hydrogen. Your goal is to group these threads to form water molecules. There is a barrier where each thread has to wait until a complete molecule can be formed. Hydrogen and oxygen threads will be given releaseHydrogen and releaseOxygen methods respectively, which will allow them to pass the barrier. These threads should pass the barrier in groups of three, and they must be able to immediately bond with each other to form a water molecule. You must guarantee that all the threads from one molecule bond before any other threads from the next molecule do.

题目大意为测试环境随机生成氢原子和氧原子,当满足合成水分子所需的数量时,打印 H2O 出来,当然 HOHOHH 都可以接受。

作者:墨尔本雪球兔
链接:https://zhuanlan.zhihu.com/p/353406724
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

const LEN = 2
 ​
 type myLock struct {
     sync.Mutex
 }
 ​
 var (
     lock myLock
     set  = make(map[string]int)
 )
 ​
 func hydrogen() {
 ​
     lock.Lock()
     defer lock.Unlock()
 ​
     set["H"]++
     fmt.Println(">   H,", set)
 }
 ​
 func oxygen() {
 ​
     lock.Lock()
     defer lock.Unlock()
     set["O"]++
     fmt.Println(">   O,", set)
 }
 ​
 func synthesizeH2O() {
 ​
     for flag := false; !flag; {
 ​
         lock.Lock()
         if set["H"] >= 2 && set["O"] >= 1 {
             set["H"], set["O"] = set["H"]-2, set["O"]-1
             fmt.Println("< H2O,", set)
             flag = true
         }
         lock.Unlock()
     }
 }
 ​
 func main() {
 ​
     _ = trace.Start(os.Stderr)
     defer trace.Stop()
 ​
     for i := 0; i < LEN; i++ {
 ​
         go hydrogen()
         go hydrogen()
         go oxygen()
         go synthesizeH2O()
     }
 ​
     time.Sleep(time.Duration(100) * time.Millisecond)
 }
synthesizeH2O

1226. The Dining Philosophers / 哲学家进餐[1]

Five silent philosophers sit at a round table with bowls of spaghetti. Forks are placed between each pair of adjacent philosophers. Each philosopher must alternately think and eat. However, a philosopher can only eat spaghetti when they have both left and right forks. Each fork can be held by only one philosopher and so a philosopher can use the fork only if it is not being used by another philosopher. After an individual philosopher finishes eating, they need to put down both forks so that the forks become available to others. A philosopher can take the fork on their right or the one on their left as they become available, but cannot start eating before getting both forks. Eating is not limited by the remaining amounts of spaghetti or stomach space; an infinite supply and an infinite demand are assumed. Design a discipline of behaviour (a concurrent algorithm) such that no philosopher will starve; i.e., each can forever continue to alternate between eating and thinking, assuming that no philosopher can know when others may want to eat or think.

题目大意为一帮哲学家实现了财富自由,有钱有闲,吃饱了就开始思考宇宙和谐的问题,

教材常见例题「哲学家进餐」便是条件锁的经典母题,为所有多线程面试题的终极根据地。

哲学家问题的同步步骤 © 所有

如图本题并不复杂,但作为 Go 语言面试,笔者选用其标准库的 Goroutine 同步句柄,channel 来作为 5 个餐具的同步信号 var forks [N]chan int,而对应地使用 channel 配套的控制流 for { select { case... }} 来实现哲学家进餐的同步操作:

const N = 5var forks [N]chan int
 ​
 func requestForks(ID int) {
 ​
     leftFork, rightFork := (ID+N-1)%N, (ID+N+1)%N
 ​
     for {
         select {
         case forks[leftFork] <- ID:
             select {
             case forks[rightFork] <- ID:
 ​
                 sleepTime := rand.Intn(10) + 10
                 fmt.Printf("Philosopher %d picks both forks and eats for %d milliseconds.
", ID, sleepTime)
                 time.Sleep(time.Duration(sleepTime) * time.Millisecond)
 ​
                 <-forks[leftFork]
                 <-forks[rightFork]
 ​
                 return
             default:
                 fmt.Printf("Philosopher %d can't pick his right fork then gave up.
", ID)
                 <-forks[leftFork]
             }
         default:
             fmt.Printf("Philosopher %d can't pick both forks then gave up.
", ID)
         }
 ​
         time.Sleep(time.Duration(rand.Intn(100)) * time.Millisecond)
     }
 }
 ​
 func main() {
 ​
     _ = trace.Start(os.Stderr)
     defer trace.Stop()
 ​
     for i := 0; i < N; i++ {
         forks[i] = make(chan int, 1)
     }
 ​
     for i := 0; i < 100; i++ {
         go requestForks(rand.Intn(N))
     }
 ​
     time.Sleep(time.Duration(1000) * time.Millisecond)
 }
requestForks

所以剩下的唯一难点就是根据哲学家的编号(本题取 i = 0...4)来透过求余运算,获得该哲学家左右手的餐具编码。

参考链接:

https://www.zhihu.com/column/c_1347495366519119872

原文地址:https://www.cnblogs.com/dingxiaoqiang/p/14623724.html