英文:
How can I write a function which accept 2 functions (which return structs) and runs them concurrently?
问题
在我正在编写的一个 golang 包中,我经常需要进行两个 HTTP 请求来获取所需的数据。
我的包包含客户端函数,通常没有参数,并返回一个结构体和一个错误。
func (c Client) GetProduct() (*Product, error)
然而,我希望能够编写一个通用函数,它可以接受这两个客户端函数作为参数,同时运行它们,并简单地返回填充有来自我所访问的 API 的数据的结构体。
理想情况下,我希望函数调用看起来像这样:
struct1, struct2, err := runFunctions(client.GetProduct, client.GetSizes)
到目前为止,我已经编写了以下通用函数:https://go.dev/play/p/IA8LJqY0FPe
问题是,因为 GetProduct
和 GetSizes
都返回一个 struct
和 error
,而不是一个 interface{}
和 error
,所以在编译时我会得到以下错误:
./prog.go:54:28: cannot use client.GetProduct (value of type func() (*Product, error)) as type func() (interface{}, error) in argument to runFunctions
./prog.go:54:47: cannot use client.GetSizes (value of type func() (*Sizes, error)) as type func() (interface{}, error) in argument to runFunctions
我的问题是如何解决这个问题?在 go 中编写这样的通用函数是否可能?
同时,如果有关于如何使用并发的一般提示,我也会很感激。
英文:
Within a golang package I am writing, I often have to make 2 HTTP requests to get the data I require.
My package contains client functions, which usually have 0 arguments and return a struct and an error.
func (c Client) GetProduct() (*Product, error)
However, I would like to be able to write a generic function, which could accept two of these client functions as arguments, run them concurrently and simply return the structs filled with data from the API I am hitting.
Ideally, I would like the function call to look like this:
struct1, struct2, err := rrunFunctions(client.GetProduct, client.GetSizes)
So far I have written the following generic function: https://go.dev/play/p/IA8LJqY0FPe
The problem is that because GetProduct
and GetSizes
both return a struct
and error
rather than an interface{}
and error
I get the following error at compile time:
./prog.go:54:28: cannot use client.GetProduct (value of type func() (*Product, error)) as type func() (interface{}, error) in argument to runFunctions
./prog.go:54:47: cannot use client.GetSizes (value of type func() (*Sizes, error)) as type func() (interface{}, error) in argument to runFunctions
My question is how do I get past this? Is writing a function generic as this possible in go?
Any general tips on using concurrency in this manner would also be appreciated.
答案1
得分: 3
是的,使用泛型是可以实现的。在函数的返回类型中使用两个类型参数:
func runFunctions[T1, T2 any](
f1 func() (T1, error),
f2 func() (T2, error),
) (res1 T1, res2 T2, err error) {
var wg sync.WaitGroup
wg.Add(2)
var err1, err2 error
go func() {
defer wg.Done()
res1, err1 = f1()
}()
go func() {
defer wg.Done()
res2, err2 = f2()
}()
wg.Wait()
// 检查错误
if err1 != nil {
err = err1
return
}
if err2 != nil {
err = err2
return
}
return
}
注意:
- 使用
defer
调用wg.Done()
- 必须在
wg.Wait()
之后检查错误!
使用它也变得更简单,因为你不必处理类型断言:
func main() {
defer timeTrack(time.Now(), "Fetching products with sizes")
fmt.Println("Starting synchronous calls...")
client := &Client{}
prodModel, sizesModel, err := runFunctions(client.GetProduct, client.GetSizes)
if err != nil {
// 返回错误
}
build(prodModel, sizesModel)
}
这将输出(在Go Playground上尝试):
Starting synchronous calls...
product 123 has size S
product 123 has size M
product 123 has size L
2009/11/10 23:00:05 Fetching products with sizes took 5s
英文:
Yes, this is possible using generics. Use 2 type parameters for the 2 return types of the functions:
func runFunctions[T1, T2 any](
f1 func() (T1, error),
f2 func() (T2, error),
) (res1 T1, res2 T2, err error) {
var wg sync.WaitGroup
wg.Add(2)
var err1, err2 error
go func() {
defer wg.Done()
res1, err1 = f1()
}()
go func() {
defer wg.Done()
res2, err2 = f2()
}()
wg.Wait()
// check errors
if err1 != nil {
err = err1
return
}
if err2 != nil {
err = err2
return
}
return
}
Notes:
- Use
defer
to callwg.Done()
- You must check errors after
wg.Wait()
!
Using it also becomes simpler as you don't have to deal with type assertions:
func main() {
defer timeTrack(time.Now(), "Fetching products with sizes")
fmt.Println("Starting synchronous calls...")
client := &Client{}
prodModel, sizesModel, err := runFunctions(client.GetProduct, client.GetSizes)
if err != nil {
// return error
}
build(prodModel, sizesModel)
}
This will output (try it on the Go Playground):
Starting synchronous calls...
product 123 has size S
product 123 has size M
product 123 has size L
2009/11/10 23:00:05 Fetching products with sizes took 5s
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论