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.

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

报告相同问题?

悬赏问题

  • ¥15 平板录音机录音问题解决
  • ¥15 请问维特智能的安卓APP在手机上存储传感器数据后,如何找到它的存储路径?
  • ¥15 (SQL语句|查询结果翻了4倍)
  • ¥15 Odoo17操作下面代码的模块时出现没有'读取'来访问
  • ¥50 .net core 并发调用接口问题
  • ¥15 网上各种方法试过了,pip还是无法使用
  • ¥15 用verilog实现tanh函数和softplus函数
  • ¥15 Hadoop集群部署启动Hadoop时碰到问题
  • ¥15 求京东批量付款能替代天诚
  • ¥15 slaris 系统断电后,重新开机后一直自动重启