Go 的特色不是语法的便捷,而是在工程

2023-02-08 01:00:55 +08:00
 GopherDaily

Envoy 这样的工程构建已经是非常复杂了,当然 Go 大型工程也不简单。 但是入门写 Go 基本就一条路,入门 C++ 就很依赖人的主观判断。

学了两天 bazel ,又学了一天 cmake ,加深了这个想法。

7856 次点击
所在节点    Go 编程语言
77 条回复
Nazz
2023-02-08 18:26:18 +08:00
@GeruzoniAnsasu 使用有锁队列保存任务; 任务完成后去队列拿下一个任务, 递归地调用;
CRVV
2023-02-08 19:28:08 +08:00
@GeruzoniAnsasu

package main

import (
"fmt"
"math/rand"
"time"
)

func worker(ch chan int, x int) {
d := rand.Intn(10)
time.Sleep(time.Millisecond * 10 * time.Duration(d))
ch <- x
}
func main() {
var queue []chan int

for i := 0; i < 10; i++ {
ch := make(chan int)
go worker(ch, i)
queue = append(queue, ch)
}
for _, ch := range queue {
fmt.Println(<-ch)
}

}
CRVV
2023-02-08 19:43:07 +08:00
@GeruzoniAnsasu

这个东西不难写,只不过它的写法和 JavaScript 惯用的写法可能不一样。
如果你觉得我发的这个不符合你的要求,你可以先用 Promise 写一版我来翻译成 Go
总体上 Go 不用写 "await" 这几个字母,其它和带异步且多线程的语言完全一样。当然 Go 自带的语言功能少一些,不限于异步并发关系时序这些,所有方面的功能都少。

> 有 promise 的情况下不需要这种可锁对象
多线程环境下的 promise 本身也带锁或者类似的机制。单线程的 JavaScript 是另一回事。

> 我们不能一次性等待一批 worker 全部完成,而是要时刻能分派已完成的 worker 占用的任务槽
这个和 Promise 有关系么?我没觉得用 Promise 可以简化这件事情的实现,拿到一个结果了再开下一个任务,都一样吧。

> spwaner 本身要可以等待或阻塞
没看懂,什么地方可以等待?你是指可以 await spwaner() 么?
lxdlam
2023-02-08 20:16:44 +08:00
@GeruzoniAnsasu

针对你的 task ,准备一个跟结果一直长度的 []chan 就可以了,扫一遍每个 channel 就可以针对每一个 chan 的阻塞策略,很直接。https://go.dev/play/p/YbtujcTry_J

至于 Promise ,你需要的只是一个 Task 结构,注意到结果本身是否 ready 可以依靠超时 + channel ,解决,封装一个类似的结构是 naive 的,在 github 上能找到非常多的类似的库。

可以去看一些官方 talk 理解 CPS 机制的原理,而不是尝试把某个机制 mapping 过来。
leonshaw
2023-02-08 20:23:16 +08:00
@GeruzoniAnsasu 本质上,是你后一个 promise await 了前一个。相应地 Go 里面为每个 goroutine make 一个 channel ,在写结果前等前一个 channel 就行了:

in := make(chan func() any)
out := make(chan any, 1)

go func() {
for result := range out {
consume(result)
}
}()

prevDone := make(chan struct{})
close(prevDone)

for do := range in {
done := make(chan struct{})
go func(do func() any, prevDone <-chan struct{}, done chan<- struct{}) {
result := do()
<-prevDone
out <- result
close(done)
}(do, prevDone, done)
prevDone = done
}
close(out)
lxdlam
2023-02-08 20:35:31 +08:00
@GeruzoniAnsasu

刚注意到有个 context miss 了,我也补充几个点:

- 使用 mpsc 跟 spsc 是非常简单的,一个基于 token 的 bucket 可以简单控制好 task 的数量,共用 token bucket 就可以控制每个 spawner 的数量。注意到这里也可以简单地基于 chan 封装一个,不需要所谓的阻塞队列。
- spawner/worker 基于 message passing 的 channel 可以解决所有 promise 的场景,包括超时等待等。
- 所谓的全局 channel ,js 的 microtask queue 和 marcotask queue 同样是全局的,甚至基于这两个场景你如果需要定制化 queue 的调度逻辑你需要对 runtime 有更加深入地理解,而 go 基于 token bucket 做定制可以做更多的事情。
learningman
2023-02-08 21:44:00 +08:00
@GeruzoniAnsasu #54 你别加条件,我说的实现是给你的最初版本的需求的
wangritian
2023-02-08 23:12:27 +08:00
你们不要再打了啦
swulling
2023-02-08 23:19:28 +08:00
编译一个 envoy 三个小时,醉了。
StevenRCE0
2023-02-09 00:12:04 +08:00
err 判空不算大问题,但是显然是 throwable 更适合工程啊……
人们在改进错误处理,然后到某些选手这儿直接就说不出错误不就行了,属实流汗黄豆。
nino
2023-02-09 00:24:29 +08:00
@GeruzoniAnsasu 首先没有细看你的需求。但是 go 只是标准库不提供 async await Promise 这些并发原语而已,要模拟出来很简单的啊,有了之后不就和你写 JS 一样了。作为 JS 和 Go 都写过的人,可以负责任的讲,Go 并发这块灵活性比 JS 强多了,可以写出很有表现力的代码。
goroutine + chan + sync 包里那堆东西,什么并发程序都能写出来。
realpg
2023-02-09 01:01:37 +08:00
@8355 #43
现在 golang 也有大量瞎糊一通,各种 panic ,然后 recovery 兜底
dbskcnc
2023-02-09 10:09:30 +08:00
什么舒服 /合适就用什么,大部分情况,我 go 用得挺舒服的. c/c++ 确实麻烦很多
xsen
2023-02-09 11:04:06 +08:00
@dbskcnc #72 前后用过很多语言,如 c/c++/python/java/javascript/dart 等等,到现在的 go ,相对来说用 go 的体验是最舒服的——不管是开发、调试,还是打包部署诸如此类。当然,也包括跨平台、交叉编译等
zxCoder
2023-02-24 16:00:03 +08:00
便捷,是大便的便吧
echoless
2023-02-28 10:29:21 +08:00
@5h4nh #46 sudog 这命名真是妙啊, 没有多少英文背景的人是无法体会的.

然而, 能与之媲美的就是 身份证 代码里写成 sfz 了.
echoless
2023-02-28 10:30:11 +08:00
@yazinnnn #26 纯手工, 鄙视你们这些投机取巧的

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/914114

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX