从提供SIGSEGV的频道中读取:分段违规

时间:2018-06-08 07:36:11

标签: mongodb go segmentation-fault channel sigsegv

我试图使用go-client(mgo)在mongoDB中插入文档。我创建了一个新的mongo会话,并且有两个用于通信的黑白通道例程, 频道 用于同步b / w readFile main ,而其他是将从 readFile 中的文件读取的数据传递给db编写例程 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)
        }
    }()
}

然后,启动goroutine 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
        }
}

另一个goroutine 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)
                }
            }
        }
}

现在,程序出现以下原因而恐慌:

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

我认为问题可能是我一次又一次地使用相同的结构,但是当我读到写入通道时会按值而不是通过引用进行复制。所以,它应该工作。任何帮助都将受到高度赞赏。

1 个答案:

答案 0 :(得分:1)

问题的根源在insertTxn()函数:

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

Collection.Insert()返回error类型的值,您引用其Error方法,但不要调用它(调用它看起来像{{1} }这将导致类型为Error()的值,无论如何都无法与string进行比较...),因此您的nil变量将是一个函数值,如果{{1 }返回明确的err错误值。

如果您想检查某个函数是否返回错误,请不要调用其Collection.Insert()方法,只需像这样检查nil值:

error.Error()

其他一些说明:

使用error等待goroutines,if err := txnColl.Insert(txn); err != nil { panic(err) } 可能适用于演示,但对于&#34;制作&#34;它真的很糟糕码。有关示例,请参阅Prevent the main() function from terminating before goroutines finish in GolangSolving goroutines deadlock

同时在sync.WaitGroup末尾注册time.Sleep()功能,以及#34; catch&#34;恐慌没有效果:

defer

这应该是main()中的第一个(或更早但绝对不是最后一个)呼叫。此外,如果您只是打印错误,它也是不必要的,因为还会打印出未恢复的恐慌。如果您打算处理这种情况,请使用 defer func() { if r := recover(); r != nil { fmt.Println(r) } }()

同样在main()读取文件时,您还应该检查recover(),因为文件可能没有您期望的那么多行,在这种情况下您可能会提前中断。