Golang中如何从goroutine返回

Golang中如何从goroutine返回 我有一个函数返回多个值(字符串,错误)

connStr := s.Url
con, err := sql.Open("postgres", connStr)
if err != nil {

	log.Println(err)
	return nil, twirp.NewError(twirp.DataLoss, "DB ERROR")
}
defer con.Close()
var resId int32
err = con.QueryRow(`INSERT INTO info(userID, details) VALUES ($1, $2) RETURNING id`, 0, request.GetDetails()).Scan(&resId)
if err != nil {
	return nil, err
}

当我将这段代码包装在匿名goroutine中时

go func() {
	connStr := s.Url
	con, err := sql.Open("postgres", connStr)
	if err != nil {

		log.Println(err)
		return nil, twirp.NewError(twirp.DataLoss, "DB ERROR")
	}
	defer con.Close()
	var resId int32
	err = con.QueryRow(`INSERT INTO info(userID, details) VALUES ($1, $2) RETURNING id`, 0, request.GetDetails()).Scan(&resId)
	if err != nil {
		return nil, err
	}

}()

我的返回语句变得无效,我该如何处理这个问题?


更多关于Golang中如何从goroutine返回的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

你的 Go 协程就像是主应用程序中的一个小进程。不要期望通过 return 指令从 Go 协程中返回值 😷 最终可以使用全局变量或通道,就像这个示例中那样 😏

更多关于Golang中如何从goroutine返回的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在我看来,你遇到错误是因为使用了一个会返回值的匿名函数,而 Go 语言并未预期获取该返回值,也不知道如何处理。为什么不给函数命名并相应处理返回值呢?你需要遵循函数必须声明返回类型的规范,即 func() (返回类型)。如果这不是可选项,也可以使用全局变量,在触发返回前更改它们的值。

在Go语言中,goroutine不能直接返回值给调用它的函数。当你在匿名goroutine中使用return语句时,它只会从goroutine自身返回,而不会传递值给外部函数。

以下是几种处理goroutine返回值的方法:

方法1:使用通道(channel)传递结果

type result struct {
    resId int32
    err   error
}

func (s *YourService) yourMethod(request *YourRequest) (*YourResponse, error) {
    resultCh := make(chan result, 1)
    
    go func() {
        connStr := s.Url
        con, err := sql.Open("postgres", connStr)
        if err != nil {
            log.Println(err)
            resultCh <- result{0, twirp.NewError(twirp.DataLoss, "DB ERROR")}
            return
        }
        defer con.Close()
        
        var resId int32
        err = con.QueryRow(`INSERT INTO info(userID, details) VALUES ($1, $2) RETURNING id`, 0, request.GetDetails()).Scan(&resId)
        resultCh <- result{resId, err}
    }()
    
    // 等待goroutine完成并获取结果
    res := <-resultCh
    if res.err != nil {
        return nil, res.err
    }
    
    // 使用res.resId构建响应
    return &YourResponse{Id: res.resId}, nil
}

方法2:使用sync.WaitGroup和共享变量

import "sync"

func (s *YourService) yourMethod(request *YourRequest) (*YourResponse, error) {
    var (
        wg     sync.WaitGroup
        resId  int32
        dbErr  error
    )
    
    wg.Add(1)
    go func() {
        defer wg.Done()
        
        connStr := s.Url
        con, err := sql.Open("postgres", connStr)
        if err != nil {
            log.Println(err)
            dbErr = twirp.NewError(twirp.DataLoss, "DB ERROR")
            return
        }
        defer con.Close()
        
        err = con.QueryRow(`INSERT INTO info(userID, details) VALUES ($1, $2) RETURNING id`, 0, request.GetDetails()).Scan(&resId)
        dbErr = err
    }()
    
    wg.Wait() // 等待goroutine完成
    
    if dbErr != nil {
        return nil, dbErr
    }
    
    return &YourResponse{Id: resId}, nil
}

方法3:使用context处理超时

import "context"

func (s *YourService) yourMethod(ctx context.Context, request *YourRequest) (*YourResponse, error) {
    resultCh := make(chan result, 1)
    
    go func() {
        connStr := s.Url
        con, err := sql.Open("postgres", connStr)
        if err != nil {
            log.Println(err)
            resultCh <- result{0, twirp.NewError(twirp.DataLoss, "DB ERROR")}
            return
        }
        defer con.Close()
        
        var resId int32
        err = con.QueryRow(`INSERT INTO info(userID, details) VALUES ($1, $2) RETURNING id`, 0, request.GetDetails()).Scan(&resId)
        resultCh <- result{resId, err}
    }()
    
    select {
    case res := <-resultCh:
        if res.err != nil {
            return nil, res.err
        }
        return &YourResponse{Id: res.resId}, nil
    case <-ctx.Done():
        return nil, ctx.Err()
    }
}

推荐使用方法1(通道)或方法3(带上下文的通道),因为它们提供了更好的并发控制和错误处理机制。通道是Go语言中goroutine间通信的首选方式。

回到顶部