谁能看出来问题在哪里 #狗头
代码:
package future
import (
"context"
)
type Future[T any] interface {
Await() (T, error)
Cancel()
}
type future[T any] struct {
ret chan result[T]
ctx context.Context
cancel context.CancelFunc
}
type result[T any] struct {
dat T
err error
}
func Async[T any](fn func() (T, error)) Future[T] {
return AsyncWithContext(context.Background(), fn)
}
func AsyncWithContext[T any](ctx context.Context, fn func() (T, error)) Future[T] {
c := make(chan result[T], 1)
fctx, cancel := context.WithCancel(ctx)
go func() {
ret, err := fn()
c <- result[T]{dat: ret, err: err}
}()
return &future[T]{ret: c, ctx: fctx, cancel: cancel}
}
func (f *future[T]) Await() (T, error) {
var result result[T]
select {
case <-f.ctx.Done():
result.err = f.ctx.Err()
case ret := <-f.ret:
result = ret
}
f.cancel()
return result.dat, result.err
}
func (f *future[T]) Cancel() {
f.cancel()
}
用法是:
f := future.Async(myfunc)
// 去干其他事
result, err := f.Await()
1
buffzty 2022-08-31 11:37:16 +08:00
1. panic 就 gg
2. 这里没有限制线程数,如果做成 js 那种 应该是起一个 go 协程做事件循环 然后挨个处理. 可以放在 loop 线程里处理 也可以设置在工作线程中处理 |
3
lysS 2022-08-31 15:54:03 +08:00
你这样的 cancel 是不会其效果的,比如 fn 是 time.Sleep(time.Secont*30), 那么哪个 goroutin 始终会执行 30s
|
4
lysS 2022-08-31 15:57:10 +08:00
要想在自己的逻辑里接入 context, 必须要求业务是可拆分的,执行一段后就去检测是否 cacel
for i := 0; i < 30; i++ { time.Sleep(time.Second) select { case <-ctx.Done(): return } } |
6
lysS 2022-08-31 15:58:52 +08:00
还要,泛型和 eface 组合毫无意义,泛型和 iface 组合有较大的性能损失 https://www.infoq.cn/article/xprmcl5qbf6yvdroajyn
|
7
rrfeng OP @lysS 哦你说这个,那是没法取消 fn 的执行,但是 Await() 是会返回的。这个需要调用方传过来的 fn 里自带 context 。
|
12
pastor 2022-09-01 14:07:21 +08:00
协程本身就比 async/await 易用、可读性强,OP 搞这种玩意可以加深下自己对协程之类的玩法,但如果真应用到业务里,那就是坑队友了。
我昨天看了这个帖子标题手滑点进来都没看内容就直接就给关闭了,今天发现竟然有人回复,就又点进来 本末倒置的玩法,不值得浪费时间,奉劝各位早点散了吧 |
14
pastor 2022-09-01 18:26:46 +08:00
@rrfeng
第一,如果没有先后顺序,那有序调用也是满足要求的,for 循环挨个调用就可以 第二,如果有性能要求,同时去请求 10 个才能满足性能,那 wg.Add(10) go func() { defer wg.Done() ... } 也比 async/await 可读性舒服得多,如果这种异步量大这里可以用协程池而不是直接 go 对于异步理解比较到位的人二院,async/await 并不比 Promise 之类算是改进,相比于 go 可读性就更不直观了 |
15
pastor 2022-09-01 18:31:17 +08:00
还有就是,如果你的业务依赖这种同时多个异步的,最麻烦的地方并不是封装这种 async/await 的绕脑的写法,而是实际场景中每个异步请求可能失败后怎么处理。
这对于不同的业务场景没有固定答案,比如爬虫或者什么,失败了也影响不大;但是对于具有事务性要求的业务,这种同时依赖多个异步远不如串行顺序处理好。对性能有很高要求的八成也应该是依赖自家的基础设施,这种如果还能设计成同时多个异步,那说明你们整体架构已经出问题了、比如微服务拆分得非常不合理,这种要治病得从架构顶层往下梳理而不是脚疼医脚。 |
16
pastor 2022-09-01 18:35:44 +08:00
go 的哲学,就是让大家从语法语义中解放出来,这种 async/await 的设计,其实本质上都不算是 lib 封装了,而是更偏于语法语义的语法糖的设计。不管花多少时间玩这种东西,到头来总有一天会想明白,发现竹篮打水。越早回头是岸越划算
|
18
rrfeng OP @pastor 要不是楼主是我,我还以为楼主在那高喊『我用 go 协程实现了超牛逼的 async/await 语法』呢。就是个语法糖,没必要扯什么 go 哲学和架构设计吧。
|
19
pastor 2022-09-02 16:57:14 +08:00
@rrfeng 我只是劝你别研究这种吃力不讨好的东西了,如果你目前阶段的修为 get 不到,就忽略我说的吧。期待未来的某天或许你会恍然大悟
|
20
pastor 2022-09-02 16:59:11 +08:00
这玩意相比与 goroutine 是倒退,跟你帖子主题说自己搞的这个东西是否牛逼没关系。
|
22
pastor 2022-09-02 19:19:32 +08:00
@rrfeng
这不是优越感,只是我这个人说话比较实在并且直接,你听了可能会不舒服而已。 至于为什么这样不懂得客气,是因为有过太多因为客气委婉、别人反倒以为自己没问题,所以我不想再客气了,有问题就尖酸刻薄地指出,至少对于技术本身,是中肯切实的 批判跟优越感也没有直接关系。 批判纯粹是因为你做的这个语法糖是一种倒退,如果没有其他人参与讨论我就不会来留言了,但是看到其他人也参与了讨论并且没有意识到这种语法糖是倒退,这就可能导致有更多人被误导。 同样有一些其他人参考其他语言做一些对于 go 而言是倒退的东西,如果力所能及,我也都会献上一些建议不要这样做的刻薄说辞 但我不只是空口乱喷,讲了一些点的,OP 要是能静下心来回到技术本身,对自己是有好处的 OP 不要纠结我的说话方式,你就当我是个没礼貌的小学生无视我的不客气好了。对其他人也一样,每个人隔三差五总会遇到让自己不舒服的人,我们没法改变环境,但是能适应环境,只要不是切实伤害,自己内心强大就无所谓别人客气不客气了 已经好些人说我刻薄之类的了,我自己也知道并且欣赏自己的刻薄。刻薄并不是什么坏事情,这世界,总是需要有一些刻薄的人的 良药苦口,忠言逆耳,认真思考技术就行了,共勉 |
24
rix 2022-09-09 16:22:36 +08:00
你需要 [tomb]( https://pkg.go.dev/gopkg.in/tomb.v2)
可以看這篇介紹: http://blog.labix.org/2011/10/09/death-of-goroutines-under-control 你似乎注意到了 Go Routine 的一大痛點,也就是沒有返回值和錯誤信息。 Tomb 提供了一個描述異步過程生命週期的類型,可以很好地管理異步生命週期。 但是 Tomb 仍然只是最基礎的生命週期描述符,它本身不具備多個異步生命週期的函數式組合。 因此我寫了 [go-rx]( https://github.com/go-rx/rx) 你可以看看,是基於 Tomb 的,結合了 Rx ( Reactive Extension )異步函數式語言的異步生命週期管理和組合用的庫。你可以併發多個 Go Routine 然後合併它們的返回值和錯誤信息,可以使用函數式的語言進行 piping 等等。 可以看我這篇文章瞭解更多: https://dev.to/rix/rx-with-go-generics-2fl6 |