dt102282 2018-06-08 07:36
浏览 43
已采纳

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

I was trying to insert documents in the mongoDB using go-client (mgo). I create a new mongo session, and two channels for communcaition b/w go-routines, channel is used to sync b/w readFile and main, while other is to pass data read from file in readFile to db write routine 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)
        }
    }()
}

Then, goroutine readFile is started which starts reading from an input file, and writing the data to txnChannel channel. After completion, it marks the completion by writing to channel channel.

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('
')
            str = strings.TrimSuffix(str, "
")
            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
        }
}

The other goroutine insertTxn, creates a reference to txn collection, and keep listening on the txnChannel channel, and writes the data it receives to the mongo collection.

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, the program panics with following reason:

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

I believe the issue might be that I am using the same struct again and again, but as I read that writing to a channel does copy by value, rather than by reference. So, it should work. Any help would be highly appreciated.

  • 写回答

1条回答 默认 最新

  • douyu1990 2018-06-08 08:12
    关注

    The source of the problem is in insertTxn() function:

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

    Collection.Insert() returns a value of type error, and you refer to its Error method, but you don't call it (calling it would look like Error() which would result in a value of type string which cannot be compared to nil anyway...), so your err variable will be a function value, which panics if Collection.Insert() returns an explicit nil error value.

    If you want to check if some function returns an error, don't call its error.Error() method, simply check the error value itself like this:

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

    Some other notes:

    Use sync.WaitGroup to wait for goroutines, time.Sleep() may be ok for a demo, but it's really bad for "production" code. For an example, see Prevent the main() function from terminating before goroutines finish in Golang and Solving goroutines deadlock.

    Also registering a defer function at the end of main() to "catch" panics has no effect:

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

    This should be the first (or an earlier but definitely not the last) call in main(). Also if you just print the error, it's needless as a panic not recovered from will also be printed. Use recover() if you intend to handle the situation.

    Also in readFile() when reading from the file, you should also check for io.EOF, as the file may not have as many lines as you expect it to, in which case you may break early.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

悬赏问题

  • ¥60 版本过低apk如何修改可以兼容新的安卓系统
  • ¥25 由IPR导致的DRIVER_POWER_STATE_FAILURE蓝屏
  • ¥50 有数据,怎么建立模型求影响全要素生产率的因素
  • ¥50 有数据,怎么用matlab求全要素生产率
  • ¥15 TI的insta-spin例程
  • ¥15 完成下列问题完成下列问题
  • ¥15 C#算法问题, 不知道怎么处理这个数据的转换
  • ¥15 YoloV5 第三方库的版本对照问题
  • ¥15 请完成下列相关问题!
  • ¥15 drone 推送镜像时候 purge: true 推送完毕后没有删除对应的镜像,手动拷贝到服务器执行结果正确在样才能让指令自动执行成功删除对应镜像,如何解决?