mongodb - 从提供 SIGSEGV 的通道中读取:分段违规
问题描述
我试图使用 go-client (mgo) 在 mongoDB 中插入文档。我创建了一个新的 mongo 会话,以及两个用于通信 b/w go-routines 的通道,通道用于同步 b/w readFile和main ,而另一个是将readFile中的文件读取的数据传递给 db write 例程 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通道。完成后,它通过写入通道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('\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]
我相信问题可能是我一次又一次地使用相同的结构,但是当我读到写入通道时确实按值复制,而不是按引用复制。所以,它应该工作。任何帮助将不胜感激。
解决方案
问题的根源在于insertTxn()
功能:
if err := txnColl.Insert(txn).Error; err != nil {
panic(err)
}
Collection.Insert()
返回一个 type 的值error
,并且您引用它的Error
方法,但您不调用它(调用它看起来Error()
会导致 type 的值无论如何string
都无法比较nil
......),所以您的err
变量将是一个函数值,如果Collection.Insert()
返回一个显式的nil
错误值,就会发生恐慌。
如果要检查某个函数是否返回错误,请不要调用它的error.Error()
方法,只需error
像这样检查值本身:
if err := txnColl.Insert(txn); err != nil {
panic(err)
}
其他一些注意事项:
用于sync.WaitGroup
等待 goroutine,time.Sleep()
对于演示可能没问题,但对于“生产”代码来说真的很糟糕。例如,请参阅在 Golang 中防止 main() 函数在 goroutines 完成之前终止和解决 goroutines deadlock。
defer
在“catch”恐慌结束时注册一个函数main()
也没有效果:
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
}
}()
这应该是第一次(或更早但绝对不是最后一次)调用main()
. 此外,如果您只是打印错误,则没有必要,因为也将打印未恢复的恐慌。recover()
如果您打算处理这种情况,请使用。
同样在readFile()
从文件中读取时,您还应该检查io.EOF
,因为文件可能没有您期望的那么多行,在这种情况下您可能会提前中断。
推荐阅读
- python - 调用函数时增加小部件创建位置的问题
- php - 显示进度并等到在 PHP 中执行 exec() 函数
- javascript - 为什么 Math.PI 被标记为不可枚举?
- java - Selenium - Java - 如何实现 XML 变量
- javascript - Node.js Mongo DB 查找查询无法按预期工作
- python - 在python中将没有时间戳的多行日志消息合并为一行
- javascript - 太多的事件处理程序会影响 React 应用程序的性能吗?
- python - OS X中python中的背景颜色没有改变
- python - GoogleTrans:AttributeError:“NoneType”对象没有属性“组”
- ios - 通过 Charles 代理查看 iOS 应用程序的网络流量