You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言中如何让main()等待init()完成MongoDB连接?

问题分析与修复方案

首先来说你的代码为什么失效,主要有两个核心问题:

1. 变量遮蔽(Variable Shadowing)导致全局client未被正确赋值

你在init()函数里写了:

client, err := mongo.Connect(context.TODO(), clientOptions)

这里的:=会在init函数的局部作用域内重新声明一个client变量,完全覆盖了全局定义的var client *mongo.Client。这就导致全局的client始终是nil,如果后续你在main里尝试使用这个全局变量,就会触发空指针引用错误。

2. 无缓冲通道的阻塞问题

你创建的是无缓冲通道var c = make(chan *mongo.Client),而init()函数是在main()执行前就运行的。当init()里执行c <- client时,此时还没有任何goroutine在接收通道的数据,无缓冲通道的发送操作会一直阻塞,直接导致程序无法正常进入main()函数,这也是你代码达不到预期的原因之一。


修复方案

如果你坚持要拆分init()和main()的逻辑,这里有两种靠谱的实现方式:

方式一:修复变量遮蔽,配合缓冲通道

先解决变量遮蔽问题,然后把通道改成带缓冲的,避免init()发送时阻塞:

package main

import (
	"context"
	"fmt"
	"log"

	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type Trainer struct {
	Name string
	Age  int
	City string
}

var client *mongo.Client
// 创建带1个缓冲的通道,避免发送时阻塞
var c = make(chan *mongo.Client, 1)

func init() {
	clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
	// 不要用:=,直接给全局client赋值,单独声明err变量
	var err error
	client, err = mongo.Connect(context.TODO(), clientOptions)
	if err != nil {
		log.Fatal(err)
	}

	err = client.Ping(context.TODO(), nil)
	if err != nil {
		log.Fatal(err)
	}

	c <- client
}

func main() {
	receivedClient := <-c
	fmt.Println("Successful connection, client:", receivedClient)
	// 后续可以用receivedClient或者全局client操作MongoDB
}

方式二:用goroutine在init里异步初始化,配合WaitGroup

如果不想用通道,也可以用sync.WaitGroup来确保main()等待初始化完成:

package main

import (
	"context"
	"fmt"
	"log"
	"sync"

	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type Trainer struct {
	Name string
	Age  int
	City string
}

var client *mongo.Client
var wg sync.WaitGroup

func init() {
	wg.Add(1)
	go func() {
		defer wg.Done()
		clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
		var err error
		client, err = mongo.Connect(context.TODO(), clientOptions)
		if err != nil {
			log.Fatal(err)
		}

		err = client.Ping(context.TODO(), nil)
		if err != nil {
			log.Fatal(err)
		}
	}()
}

func main() {
	wg.Wait() // 等待初始化完成后再执行业务逻辑
	fmt.Println("Successful connection, client:", client)
	// 现在可以安全使用client了
}

额外建议

其实在Go里,init()函数本身就是用来做初始化的,如果你只是想拆分初始化和业务逻辑,没必要强行用通道或者goroutine,直接在init()里正确初始化全局client,然后在main()里直接使用就好,这样代码更简洁:

package main

import (
	"context"
	"fmt"
	"log"

	"go.mongodb.org/mongo-driver/mongo"
	"go.mongodb.org/mongo-driver/mongo/options"
)

type Trainer struct {
	Name string
	Age  int
	City string
}

var client *mongo.Client

func init() {
	clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")
	var err error
	client, err = mongo.Connect(context.TODO(), clientOptions)
	if err != nil {
		log.Fatal(err)
	}

	err = client.Ping(context.TODO(), nil)
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	fmt.Println("Successful connection")
	// 直接使用全局client操作MongoDB
}

内容的提问来源于stack exchange,提问作者Marios Simou

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:52:39