Using function to process synchronously and asynchronously both and get results in golang

huangapple go评论96阅读模式
英文:

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 &quot;fmt&quot;

type Test struct {
	a int
	b int
}

func (t *Test) exec(responseChannel *chan bool, errChannel *chan error) (bool, error) {
	response := false
	if responseChannel != nil &amp;&amp; errChannel != nil {
		*responseChannel &lt;- response
		*errChannel &lt;- nil
	}
	return response, nil
}

func (t *Test) Compute(sync bool) {
	if !sync {
		responseChannel := make(chan bool)
		errChannel := make(chan error)

		go t.exec(&amp;responseChannel, &amp;errChannel)

		fmt.Println(&lt;-responseChannel)
		fmt.Println(&lt;-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 &lt;- r
		}()
		fmt.Println(&lt;-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 = &lt;-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.

huangapple
  • 本文由 发表于 2023年6月18日 00:02:51
  • 转载请务必保留本文链接:https://go.coder-hub.com/76497013.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定