我有要验证的字段的简单 struct .稍后,这将是一个嵌套得很深的 struct ,因为现在它很简单.

type SpotRequest struct { //holder struct
    Location Location
}
type Location struct { // target struc, this is going to be validated
    Longitude float64 
    Latitude  float64
}
type Error struct { //return to caller of a service
    Field  string
    Reason string
}

这是一个异步函数(缺少更好的词):

func validateLocation(location *Location, ch chan []Error) {

    var errors []Error
    //some custom logic which will return list of errors
    errr := &Error{
        Field:  "myField",
        Reason: "value must start with upper case",
    }
    ch <- append(s, *errr)
}

主代码:

ch := make(chan []Error, 1) //I have set for the second argument value of 1 since I will be returning single list with zero or more Error structs
go validateLocation(&spotRequest.Location, ch)
for i := 0; i < 1; i++ { //iterating one time since I need to get one value from channel ch
    select {
    case msg1 := <-ch:
        fmt.Printf("Result from location validations: %v \n", msg1)
    default:
        fmt.Println("Should not print")
    }
}

流只在DEFAULT子句中结束.我哪里错了?第一次和大猩猩在一起.

推荐答案

让我们从语言规范中的几句话开始,首先是go句:

"go"语句作为同一地址空间内独立的并发控制线程或Goroutine启动函数调用的执行.

其次是select中的default case:

如果一个或多个通信可以继续,则通过统一伪随机 Select Select 可以继续进行的单个通信.否则,如果存在默认情况,则 Select 该情况.

将这一点应用于您的程序,首先执行go validateLocation(&spotRequest.Location, ch).这"开始执行"函数;单词"Start"很重要,Goroutine是独立的,所以当for循环开始时,可能是:

  • 尚未运行Goroutine函数中的任何代码.
  • Goroutine函数中的一些代码已经运行(它可能同时在另一个内核上运行).
  • Goroutine函数中的所有代码都已运行.

您不应该对这三个选项中哪一个是正确的做出任何假设.Goroutine是独立的;如果没有某种形式的同步(例如,通道、等待组、互斥体等),您就无法知道它们的状态.

使用您的特定代码(使用此版本的GO编译器并在您的特定环境中),Goroutine还没有运行到在main函数达到select之前执行ch <- append(s, *errr)的程度.所以看看这一点:

select {
case msg1 := <-ch:
    fmt.Printf("Result from location validations: %v \n", msg1)
default:
    fmt.Println("Should not print")
}

因为大猩猩还没有运行ch <- append(s, *errr),所以ch中没有任何东西,所以该 case 是"未准备好".这意味着,根据上面的规范摘录, Select 了缺省情况.在select之前的Adding time.Sleep(time.Nanosecond)很可能(不保证!)为Goroutine提供完成的时间(意味着case msg1 := <-ch:将被 Select ).

"修复"这个问题的快捷方法(如果你知道总会有一个,而且只有一个东西在频道上发送的话)是调到remove the default clause.这将导致select等待信道上有可用的东西.

然而,这可能是您现在想要的,因为我猜您需要应对:

  • 无错误(通道上未发送任何内容)
  • 一个错误
  • 多个错误(如果发生这种情况,是否应该在第一个错误发生时停止其他 routine ?)

因此,您当前的算法可能是不够的(您并没有真正列出您的需求,所以这只是一个猜测).

在这一点上,值得一提的是,您可能正在进行过早的优化.除非您知道您的验证 routine 将成为瓶颈(例如,它们执行缓慢的数据库查询),否则我建议您尽可能地编写最简单的算法(没有Goroutine!).如果结果表明这很慢,那么您可以对其进行优化(并且您有一个基准可供参考--不要认为添加Goroutine就会加快速度!)添加Goroutine会使您的代码变得更加复杂,您需要考虑同步和类似于race conditions的东西.

话虽如此,如果需要并行运行验证,那么可以考虑使用类似errgroup的代码.文档中的"JustErrors"示例似乎非常符合您的需求.Here是使用这一点的代码的修改版本(如果没有更多的验证 routine ,就没有那么有用了!)

g := new(errgroup.Group)
g.Go(func() error { return validateLocation(&Location{}) })

if err := g.Wait(); err != nil { // Waits for either an error or completion of all go routines
    panic(fmt.Sprintf("Error: %s", err))
}
fmt.Println("successful")

Go相关问答推荐

有没有办法通过Go cmdline或IDE(IntelliJ)找出我的 struct 实现了什么接口?

为什么(编码器).EncodeElement忽略";,innerxml";标记?

从MySQL/GO表获取行数据

为什么 `go mod` 占用了另一个磁盘上的空间而不是我的 GOPATH?

go-jwt 令牌验证错误 - 令牌签名无效:密钥类型无效

按位移计算结果中的差异

Golang text/template中的startswith函数 - 入门教程

使用Cookie身份验证的Gorilla Golang Websocket优化

Go test "-run -" 标志执行测试更快

替换字符串中的最后一个字符

从单词中删除特殊字符

如何以干净的方式在中间件中注入 repo 或服务?

我突然无法再将我的 GoLang 应用程序部署到 Google AppEngine

设置指向空接口的指针

为什么 `append(x[:0:0], x...)` 将切片复制到 Go 中的新后备数组中?

Golang 构建多平台问题

递归数据 struct 解组在 Go Lang Protobuf 中给出错误无法解析无效的线格式数据

Go generics:我会在哪里使用 any 而不是 interface{}?

是否存在一个Go泛型类型约束,该约束捕获了将类型用作映射中的键的能力?

Go 泛型是否与 LINQ to Objects 等效?