我编写 Go 一段时间了,但最近才需要实际利用 goroutine/异步操作。我很难理解事情应该如何运作。
project doSomething
,需要在 for 循环中运行一些 go 例程。fatal error: all goroutines are asleep - deadlock!
但是当我认为我已经解决了我的命令似乎永远挂起时(我认为当其中一个 go 例程遇到错误时就会发生这种情况。 ..?).有人可以帮助我理解我应该在这里实现的模式吗?
main.go
func main() {
ctx := context.Background()
if err := cmd.NewRootCommand().ExecuteContext(ctx); err != nil {
log.Err(err)
os.Exit(1)
}
}
另一个文件.go
func NewRootCommand() *cobra.Command {
rootCmd.AddCommand(newProjectCmd())
return rootCmd
}
func newProjectCmd() *cobra.Command {
projCmd := &cobra.Command{
Use: "project",
Short: "Interact with groups",
}
projCmd.AddCommand(newDoSomethingCmd())
return projCmd
}
func newDoSomethingCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "doSomething",
Run: func(cmd *cobra.Command, args []string) {
// stuff happens here, but no go routines or anything.
err := project.DoSomething(cmd.Context())
if err != nil {
os.Exit(1)
}
},
}
return cmd
}
lastfile.go(项目包)
func DoSomething(ctx context.Context) error {
// lots of stuff happens
err := firstFunction(ctx)
if err != nil {
return err
}
return nil
}
func firstFunction(ctx context.Context) error {
// stuff here no go routines
err := functionWithGoRoutines(ctx)
if err != nil {
return err
}
// gets called multiple times with different parameters -- at this point I want the above to FINISH though before moving on to here
err := functionWithGoRoutines(ctx)
if err != nil {
return err
}
return nil
}
// UPDATED
func functionWithGoRoutines(ctx context.Context, items []string) error {
errors := make(chan error, 0)
var wg sync.WaitGroup
for _, item := range items {
wg.Add(1)
i := item
go func() {
defer wg.Done()
out, err := doSomethingToItemThatCanReturnError(ctx, i)
if err != nil {
errors <- fmt.Errorf("erro: %v", err)
return
}
fmt.Println(out)
}()
}
wg.Wait()
close(errors)
var err error
if len(errors) > 0 {
err = fmt.Errorf("multiple errors occurred: %v", errors)
}
return err
}
如果这个问题已经在其他地方得到回答而我错过了,请告诉我!据我所知,没有什么可以准确描述我的情况。如果我想做的事情有任何不清楚的地方,请告诉我!
我的最终目标是拥有一个循环遍历切片的函数,并为切片中的每个项目启动一个 go 例程。我希望每个项目的 go 例程在退出外部函数之前都有时间完成。我想收集错误(如果有)并在所有 go 例程完成后返回它们。
我还尝试在 main.go 中创建等待组,并将其一直传递到我的函数。我看到某个地方我应该在全球范围内声明它...但不确定在哪里?
如果发生错误,你的代码将会挂起,因为发生错误的 goroutine 会阻止尝试写入错误通道。没有 goroutine 从中读取数据。
你必须将等待放在 waitgroup 上或监听错误通道到它自己的 goroutine 中:
func functionWithGoRoutines(ctx context.Context, items []string) error {
errors := make(chan error, 0)
var wg sync.WaitGroup
for _, item := range items {
wg.Add(1)
i := item
go func() {
defer wg.Done()
out, err := doSomethingToItemThatCanReturnError(ctx, i)
if err != nil {
errors <- fmt.Errorf("erro: %v", err)
return
}
fmt.Println(out)
}()
}
go func() {
wg.Wait()
close(errors)
}()
errs:=make([]error,0)
for err:=range errors {
errs=append(errs,err)
}
if len(errs)==0 {
return nil
}
return errors.Join(errs...)
}