英文:
Using function to process synchronously and asynchronously both and get results in golang
问题
我想要一个函数exec
能够同时处理同步和异步的情况。
我可能会从Compute
函数中执行多个exec
的go例程。
想要检查这样编码是否正确。
package main
import "fmt"
type Test struct {
a int
b int
}
func (t *Test) exec(responseChannel *chan bool, errChannel *chan error) (bool, error) {
response := false
if responseChannel != nil && errChannel != nil {
*responseChannel <- response
*errChannel <- nil
}
return response, nil
}
func (t *Test) Compute(sync bool) {
if !sync {
responseChannel := make(chan bool)
errChannel := make(chan error)
go t.exec(&responseChannel, &errChannel)
fmt.Println(<-responseChannel)
fmt.Println(<-errChannel)
} else {
res, err := t.exec(nil, nil)
fmt.Println(res)
fmt.Println(err)
}
}
func main() {
t := Test{
10,
12,
}
t.Compute(true)
t.Compute(false)
}
在我的用例中,我将在exec
函数中进行网络调用(大多数是数据库调用)。
英文:
I wanted a function exec
to process synchronously and asynchronously both.
I might execute multiple go routines of exec
from Compute
function.
Wanted to check whether it is fine to code like this.
package main
import "fmt"
type Test struct {
a int
b int
}
func (t *Test) exec(responseChannel *chan bool, errChannel *chan error) (bool, error) {
response := false
if responseChannel != nil && errChannel != nil {
*responseChannel <- response
*errChannel <- nil
}
return response, nil
}
func (t *Test) Compute(sync bool) {
if !sync {
responseChannel := make(chan bool)
errChannel := make(chan error)
go t.exec(&responseChannel, &errChannel)
fmt.Println(<-responseChannel)
fmt.Println(<-errChannel)
} else {
res, err := t.exec(nil, nil)
fmt.Println(res)
fmt.Println(err)
}
}
func main() {
t := Test{
10,
12,
}
t.Compute(true)
t.Compute(false)
}
In my usecase I will be making network calls (most only db calls) in exec
function.
答案1
得分: 1
惯用的方法是使用调用者根据需要使用Go协程编写同步函数。以下是使用该习语重写的代码:
func (t *Test) exec() (bool, error) {
response := false
return response, nil
}
func (t *Test) Compute(sync bool) {
if !sync {
type result struct {
value bool
err error
}
c := make(chan result)
go func() {
var r result
r.value, r.err = t.exec()
c <- r
}()
fmt.Println(<-c)
} else {
res, err := t.exec()
fmt.Println(res)
fmt.Println(err)
}
}
在问题和答案中,对exec
的两次调用都是同步执行的。我假设问题提供的是一个简化的示例,真实的代码具有实际的异步性。如果不是这样的话,代码可以简化为:
func (t *Test) Compute(sync bool) {
res, err := t.exec()
fmt.Println(res)
fmt.Println(err)
}
英文:
The idiomatic approach is to write synchronous functions with the caller using Go routines as needed. Here's the code rewritten to that idiom:
func (t *Test) exec() (bool, error) {
response := false
return response, nil
}
func (t *Test) Compute(sync bool) {
if !sync {
type result struct {
value bool
err error
}
c := make(chan result)
go func() {
var r result
r.value, r.err = t.exec()
c <- r
}()
fmt.Println(<-c)
} else {
res, err := t.exec()
fmt.Println(res)
fmt.Println(err)
}
}
Both calls to exec
are executed synchronously in the question and this answer. I assume that the question presents a stripped down example and that the real code has actual asynchrony. If not, then the code can be reduced to:
func (t *Test) Compute(sync bool) {
res, err := t.exec()
fmt.Println(res)
fmt.Println(err)
}
答案2
得分: 0
简短回答:是的,这样做是可以的。
然而,你可能想考虑一些替代方案。当你开始处理更复杂的代码时,你需要不断更新exec
来处理更复杂的并发结构,比如sync.WaitGroup
,而这些更新对于同步部分来说是多余的。通常人们会编写两个函数(一个用于异步工作,一个用于同步工作),然后使用这些函数。这里是一个非常简单的示例:
func (t *Test) doAsync(out chan any, ...) {
...
out <- val
}
func (t *Test) doSync(...) any {
...
return val
}
func (t *Test) Compute(sync bool) {
var res any
if sync {
res = doSync(...)
} else {
resChan := make(chan any)
doAsync(resChan, ...)
res = <-resChan
}
}
这样,如果你需要在异步处理程序中添加更复杂的内容,同步处理程序就不需要担心它。
英文:
Short answer: Yes, this is fine.
However, you may want to consider some alternatives. When you start dealing with more complex code, you'll need to continuously update exec
to handle more complex concurrency structures, like sync.WaitGroup
, while those updates will be redundant for the synchronous portion. Usually people will write two functions (one for async work and one for sync work) and then work with those. Here's a very rough example:
func (t *Test) doAsync(out chan any, ...) {
...
out <- val
}
func (t *Test) doSync(...) any {
...
return val
}
func (t *Test) Compute(sync bool) {
var res any
if sync {
res = doSync(...)
} else {
resChan := make(chan any)
doAsync(resChan, ...)
res = <-resChan
}
}
This way, if you need to update the async handler with more complex stuff, the sync handler doesn't need to worry about it.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论