手撕 Goroutine 同步问题
1114. Print in Order 按序列印
The same instance ofFoo
will be passed to three different threads. Thread A will callfirst()
, thread B will callsecond()
, and thread C will callthird()
. Design a mechanism and modify the program to ensure thatsecond()
is executed afterfirst()
, andthird()
is executed aftersecond()
.
本题要求阻塞一众并发的 Goroutine,然后按 One Two Three
的逻辑顺序列印结果
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
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) 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
1117. H2O / 分子合成
There are two kinds of threads,oxygen
andhydrogen
. 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 givenreleaseHydrogen
andreleaseOxygen
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 出来,当然 HOH
和 OHH
都可以接受。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
作者:墨尔本雪球兔 链接: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) }
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.
题目大意为一帮哲学家实现了财富自由,有钱有闲,吃饱了就开始思考宇宙和谐的问题,
教材常见例题「哲学家进餐」便是条件锁的经典母题,为所有多线程面试题的终极根据地。
![](https://pic2.zhimg.com/v2-6689493ae205ceb79497d9721ef2ffa5_b.jpg)
如图本题并不复杂,但作为 Go 语言面试,笔者选用其标准库的 Goroutine 同步句柄,channel
来作为 5 个餐具的同步信号 var forks [N]chan int
,而对应地使用 channel
配套的控制流 for { select { case... }}
来实现哲学家进餐的同步操作:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
const N = 5 var 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.\n", 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.\n", ID) <-forks[leftFork] } default: fmt.Printf("Philosopher %d can't pick both forks then gave up.\n", 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) }
所以剩下的唯一难点就是根据哲学家的编号(本题取 i = 0...4)来透过求余运算,获得该哲学家左右手的餐具编码。
参考链接:
https://www.zhihu.com/column/c_1347495366519119872