我试图使用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]
我认为问题可能是我一次又一次地使用相同的结构,但是当我读到写入通道时会按值而不是通过引用进行复制。所以,它应该工作。任何帮助都将受到高度赞赏。
答案 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 Golang和Solving goroutines deadlock。
同时在sync.WaitGroup
末尾注册time.Sleep()
功能,以及#34; catch&#34;恐慌没有效果:
defer
这应该是main()
中的第一个(或更早但绝对不是最后一个)呼叫。此外,如果您只是打印错误,它也是不必要的,因为还会打印出未恢复的恐慌。如果您打算处理这种情况,请使用 defer func() {
if r := recover(); r != nil {
fmt.Println(r)
}
}()
。
同样在main()
读取文件时,您还应该检查recover()
,因为文件可能没有您期望的那么多行,在这种情况下您可能会提前中断。