Чтение с канала, дающего SIGSEGV: нарушение сегментации - PullRequest
0 голосов
/ 08 июня 2018

Я пытался вставить документы в mongoDB с помощью go-client (mgo).Я создаю новый сеанс монго и два канала для ч / б коммуникативных процедур, канал используется для синхронизации ч / б readFile и main , в то время как другие должны передавать данные, считанные из файла в readFile , в подпрограмму записи в БД insertTxn .

type Txn struct {
    Date time.Time
    Amt  float64
}



func main() {
    session, err := mgo.Dial("localhost")
    if err != nil {
        panic(err)
    }
    defer session.Close()

    channel := make(chan bool)
    txnChannel := make(chan Txn, 1e4)

    go readFile(txnChannel, channel)
    go insertTxn(session, txnChannel)

    <-channel
    time.Sleep(time.Second * 10) // waiting for insertTxn to finish

    defer func() {
        if r := recover(); r != nil {
            fmt.Println(r)
        }
    }()
}

Затем запускается программа readFile , которая начинает чтение из входного файла и запись данных в txnChannel канал.После завершения он отмечает завершение, записывая в канал канал .

func readFile(txnChannel chan<- Txn, channel chan<- bool) { // write only channel
        txnFile, err := os.Open("path/to/dir/txns.txt")
        if err != nil {
            panic(err)
        }
        txnReader := bufio.NewReader(txnFile)
        defer func() {
            txnFile.Close()
            channel <- true
        }()
        var data []string
        var txn Txn
        var dur int
        var str string
        for i := 0; i < 25*1e2; i++ {
            str, err = txnReader.ReadString('\n')
            str = strings.TrimSuffix(str, "\n")
            if err != nil {
                panic(err)
            }
            data = strings.Split(str, " ")
            txn.Amt, err = strconv.ParseFloat(data[1], 64)
            if err != nil {
                panic(err)
            }
            if err != nil {
                panic(err)
            }
            dur, err = strconv.Atoi(data[0])
            if err != nil {
                panic(err)
            }
            txn.Date = time.Now().Add(-time.Second * time.Duration(dur))
            txnChannel <- txn
        }
}

Другая процедура insertTxn создает ссылку на коллекцию txn и продолжает прослушивание на канале txnChannel и записывает полученные данные в коллекцию mongo.

func insertTxn(session *mgo.Session, txnChannel <-chan Txn) {
        txnColl := session.DB("happay").C("txns")
        for {
            select {
            case txn := <-txnChannel:
                if err := txnColl.Insert(txn).Error; err != nil {
                    panic(err)
                }
            }
        }
}

Now,программа паникует по следующей причине:

panic: runtime error: invalid memory address or nil pointer dereference
[signal SIGSEGV: segmentation violation code=0x1 addr=0x0 pc=0x1151f27]

Я полагаю, что проблема может заключаться в том, что я использую одну и ту же структуру снова и снова, но когда я читаю, что запись в канал действительно копирует по значению, а не поссылка.Итак, это должно работать.Любая помощь будет принята с благодарностью.

1 Ответ

0 голосов
/ 08 июня 2018

Источник проблемы в insertTxn() функции:

if err := txnColl.Insert(txn).Error; err != nil {
    panic(err)
}

Collection.Insert() возвращает значение типа error, и вы ссылаетесь на его метод Error, но вы неего вызов (вызов будет выглядеть как Error(), что приведет к значению типа string, которое в любом случае нельзя сравнить с nil ...), поэтому ваша переменная err будет значением функции, котораяпаникует, если Collection.Insert() возвращает явное nil значение ошибки.

Если вы хотите проверить, возвращает ли какая-либо функция ошибку, не вызывайте ее метод error.Error(), просто проверьте само значение errorкак это:

if err := txnColl.Insert(txn); err != nil {
    panic(err)
}

Некоторые другие заметки:

Используйте sync.WaitGroup, чтобы дождаться выполнения процедур, time.Sleep() может подойти для демонстрации, но это действительноплохо для «производственного» кода.Например, см. Предотвращение завершения функции main () до завершения выполнения goroutines в Golang и Устранение тупиковой ситуации с goroutines .

Также регистрация функции defer наконец main() для «ловли» паники не имеет никакого эффекта:

   defer func() {
        if r := recover(); r != nil {
            fmt.Println(r)
        }
    }()

Это должен быть первый (или более ранний, но определенно не последний) вызов в main().Также, если вы просто распечатываете ошибку, она не нужна, так как паника, не восстановленная, также будет напечатана.Используйте recover(), если вы намереваетесь разобраться с ситуацией.

Также в readFile() при чтении из файла также следует проверить наличие io.EOF, так как файл может содержать не так много строк, как вы ожидаетеэто, в этом случае вы можете сломаться рано.

...