如何通过通道从goroutine返回错误。

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

How to return a error from a goroutine (through channels)

问题

当我在Go中编写一个函数时,它应该返回一个值和一个错误,就像这样:

func createHashedPassword(password string) (string, error) {
  //代码
}

我想在一个goroutine中执行这个createHashedPassword函数,并且我打算通过通道传递数据。
但是我的问题是,我应该如何处理这里的错误或者在goroutine中处理错误呢?

英文:

When i write a function in Go, it should return a value and an error like

func createHashedPassword(password string) (string, error) {
  //code
}

I want to execute this createHashedPassword in a goroutine and I think to pass data via channel.
But my question is, how can I handle error here or in the goroutine?

答案1

得分: 75

常见的做法是将多个输出打包到一个结构体中,并通过单个通道一起返回。

type Result struct {
    Message string
    Error error
}

ch := make(chan Result)
英文:

It's common to bundle multiple outputs into a struct, and return them together over a single channel.

type Result struct {
    Message string
    Error error
}

ch := make(chan Result)

答案2

得分: 30

你可以传入一个错误通道和一个结果通道。

errors := make(chan error, 0)
results := make(chan string, 0)

password := "test"

go func() {
    result, err := createHashedPassword(password)
    if err != nil {
        errors <- err
        return
    }

    results <- result
}()

// 做其他事情

// 当你准备好从 goroutine 中读取时,可以这样做:
select {
    case err := <- errors:
        println(err)
    case res := <- results:
        println(res)
}
英文:

You can pass in an error channel as well as a result channel.

errors := make(chan error, 0)
results := make(chan string, 0)

password := &quot;test&quot;

go func() {
    result, err := createHashedPassword(string password)
    if err != nil {
        errors &lt;- err
        return
    }

    results &lt;- result
}()

// do something else

// When you are ready to read from goroutine do this:
select {
    case err := &lt;- errors:
        println(err)
    case res := &lt;- results:
        println(res)
}

答案3

得分: 12

以下是我翻译好的内容:

这是我两种首选的方法:

两个通道,封装

这是“两个通道”的方式,但封装成一个函数,使其看起来类似于常见的模式:

func createHashedPasswordAsynchronously(password string) (chan string, chan error) {
    resultCh := make(chan string)
    errorCh := make(chan error)

    go func(password string) {
        // 代码
        if err != nil {
            errorCh <- errors.New("计算错误")
        } else {
            resultCh <- "8badf00d"
        }
    }(password)

    return resultCh, errorCh
}

并像这样调用:

resultCh, errorCh := createHashedPasswordAsynchronously("mysecret")
select {
case result := <-resultCh:
    storeHashedPassword(result)
case err := <-errorCh:
    log.Println(err.Error())
}

匿名结构体

这是“匿名结构体”的方式,类似于@saward的答案,但不显式命名结构体成员:

go func(password string, ch chan struct {
    string
    error
}) {
    // 代码
    if err != nil {
        ch <- struct {
            string
            error
        }{"", errors.New("计算错误")}
    } else {
        ch <- struct {
            string
            error
        }{"8badf00d", nil}
    }
}("mysecret", ch)

r := <-ch
if r.error != nil {
    log.Println(r.error.Error())
} else {
    storeHashedPassword(r.string)
}
英文:

Here are my two preferred ways:

Two channels, wrapped

This is the "two channels" way, but wrapped into a function to make it look similar to the common pattern:

func createHashedPasswordAsynchronously(password string) (chan string, chan error) {
	resultCh := make(chan string)
	errorCh := make(chan error)

	go func(password string) {
		//code
		if err != nil {
			errorCh &lt;- errors.New(&quot;Does not compute&quot;)
		} else {
			resultCh &lt;- &quot;8badf00d&quot;
		}
	}(password)

	return resultCh, errorCh
}

And called like this:

resultCh, errorCh := createHashedPasswordAsynchronously(&quot;mysecret&quot;)
select {
case result := &lt;-resultCh:
	storeHashedPassword(result)
case err := &lt;-errorCh:
	log.Println(err.Error())
}

Anonymous struct

This is the "anonymous struct" way, similar to @saward's answer, but without naming the struct members explicitly:

go func(password string, ch chan struct {
	string
	error
}) {
	//code
	if err != nil {
		ch &lt;- struct {
			string
			error
		}{&quot;&quot;, errors.New(&quot;Does not compute&quot;)}
	} else {
		ch &lt;- struct {
			string
			error
		}{&quot;8badf00d&quot;, nil}
	}
}(&quot;mysecret&quot;, ch)

r := &lt;-ch
if r.error != nil {
	log.Println(r.error.Error())
} else {
	storeHashedPassword(r.string)
}

答案4

得分: 8

我重复一下JimB的说法:

type Result struct {
    Message string
    Error error
}

ch := make(chan Result)

使用两个单独的通道,一个用于结果,另一个用于错误,问题在于(据我了解)它不会直接支持并发线程。

例如,你可能会有两个线程同时发送数据,导致回复的顺序错乱。也就是说,你先从线程1接收到结果,但先从线程2接收到错误。

像JimB建议的那样创建新类型很容易,并且应该能很好地与goroutine一起使用。

英文:

(since I cannot comment yet...)

I echo what JimB said with:

type Result struct {
    Message string
    Error error
}

ch := make(chan Result)

The trouble with two separate channels, one for the result, and another for the error, is that (as I understand) it won't support concurrent threads out of the box.

You could, for example, have two threads sending data at the same time, where the replies get out of order. That is, you receive the result from thread 1 first, but the error from thread 2 first.

It's easy to create new types like JimB suggested, and should work well with goroutines.

答案5

得分: 1

一种常见的模式是将错误通道和结果通道传递给函数:

package main

import (
	"fmt"
)

func main() {
	password := "blah blah"
	resultChan := make(chan string)
	errChan := make(chan error)

	go createHashedPassword(password, resultChan, errChan)

	// 做一些其他的事情

	select {
	case err := <-errChan:
		fmt.Println(err)
	case res := <-resultChan:
		fmt.Println(res)
	}
}

func createHashedPassword(password string, resultChan chan string, errChan chan error) {
	// 进行哈希等操作的代码
	if err != nil {
		errChan <- err
		return
	}
	resultChan <- result
}

你也可以像 AndreKR 提到的那样,在 createHashedPassword 函数内部创建通道,

或者像 Ian Davis 提到的那样,在匿名 goroutine 中调用 createHashedPassword 函数。

还有其他一些可能的模式,你可以将它们与组合结构体结合起来:

type Result struct {
	err error
	msg string
}

或者

type Result struct {
	err error
	msg *string // 在空字符串可以作为真实结果的情况下,你可以使用 nil 来评估 msg
}

我还应该提到,在某些特殊情况下,我们不会返回错误,而是在那里记录错误。

package main

import (
	"fmt"
	"log"
)

func main() {
	password := "blah blah"
	resultChan := make(chan string)

	go createHashedPassword(password, resultChan)

	// 做一些其他的事情

	fmt.Println(<-resultChan) // 注意这是一个阻塞的代码。
}

func createHashedPassword(password string, resultChan chan string) {
	// 进行哈希等操作的代码
	if err != nil {
		log.Println(err) // *** 在这里我们只是记录错误,或者处理它。
	}
	resultChan <- result
}
英文:

One of the common patterns is to pass a error channel and a result channel to the function:

package main

import (
  &quot;fmt&quot;
)

func main() {
  password := &quot;blah blah&quot;
  resultChan := make(chan string)
  errChan := make(chan error)

  go createHashedPassword(password, resultChan, errChan)

  // Do some other stuff

  select {
  case err := &lt;-errChan:
	fmt.Println(err)
  case res := &lt;-resultChan:
	fmt.Println(res)
  }
}

func createHashedPassword(password string, resultChan chan string, errChan chan error) {
  // your code for hashing and stuff
  if err != nil {
	errChan &lt;- err
	return
  }
  resultChan &lt;- result
}

You can also make channels inside the createHashedPassword as AndreKR said,

or call the createHashedPassword inside an anonymous goroutine as Ian Davis said.


There is also some other possible patterns, and you can combine any of them with combined struct:

type Result struct {
  err error
  msg string
}

or

type Result struct {
  err error
  msg *string // in cases that empty string can be a true result, you can evaluate msg with nil
}

I should also mention that in some special cases, we don't return the error and just log it there.

package main

import (
	&quot;fmt&quot;
	&quot;log&quot;
)

func main() {
	password := &quot;blah blah&quot;
	resultChan := make(chan string)

	go createHashedPassword(password, resultChan)

	// Do some other stuff

	fmt.Println(&lt;-resultChan) // Be careful that it&#39;s a blocking code.
}

func createHashedPassword(password string, resultChan chan string) {
	// your code for hashing and stuff
	if err != nil {
		log.Println(err) // *** Here we just log the error, or handle it.
	}
	resultChan &lt;- result
}

huangapple
  • 本文由 发表于 2014年8月5日 22:57:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/25142016.html
匿名

发表评论

匿名网友

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

确定