Как заставить main () ждать, пока init () не завершится в golang? - PullRequest
0 голосов
/ 19 октября 2019

Когда я запускаю свой main.go со следующим кодом, он работает нормально, и клиент подключается к базе данных mongo.

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
}

func main() {
    clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")

    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)
    }
    fmt.Println("Successful connection")
}

Однако, когда я пытаюсь разделить логику кода между init() иmain() goroutines, я получаю ошибку ссылки на память, что является нормальным, потому что main выполняется до того, как init фактически имеет соединение tcp с БД. Я попытался соединить их с каналом, но он не работает, как ожидалось.

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
var c = make(chan *mongo.Client)

func init() {
    // configures the connection options
    clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")

    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() {
    fmt.Println(<-c)
}

Поскольку у меня недостаточно опыта работы с golang, кто-нибудь может объяснить мне, почему мое решение не работает и как я могу это исправить? Я хочу, чтобы init () и main () были разделены, если это возможно.

1 Ответ

3 голосов
/ 19 октября 2019

Ваш код имеет три проблемы:

1) Функции init и main не являются goroutines, они запускаются последовательно при запуске программы. Поэтому нет смысла использовать небуферизованный канал.

2) Переменный клиент повторно объявлен в init с помощью оператора ': ='.

3) Я бы не рекомендовал инициализировать клиентское соединение в init. при необходимости поместите код в вспомогательную функцию и вызовите его из main.

Фиксирование, охватывающее (1) и (2) и исключающее (3):

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
// We don't need a channel since init and main are not goroutines. Both
// are executed sequentially in the main thread.
// var c = make(chan *mongo.Client)


// It is not recommended to use init to setup database connections. It
// is definitely part of the program and belongs into main.
func init() {
    // configures the connection options
    clientOptions := options.Client().ApplyURI("mongodb://localhost:27017")

    // Don't redeclare client here. We have however to declare err.
    var err error
    // Note that we use assignment (=) and not declaration (:=).
    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() {
    // Use the global client variable instead reading from the channel.
    fmt.Println(client)
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...