从提供 SIGSEGV 的通道读取:分段违规
Reading from channel giving SIGSEGV: segmentation violation
我试图使用 go-client (mgo) 在 mongoDB 中插入文档。我创建了一个新的 mongo 会话,以及两个用于通信的通道 b/w go-routines,channel 用于同步b/w readFile 和 main, 同时另一种是将从 readFile 中的文件读取的数据传递给 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)
}
}()
}
然后,goroutine readFile 启动,开始从输入文件读取数据,并将数据写入 txnChannel频道。完成后通过写入channelchannel.
来标记完成
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()
returns 一个 error
类型的值,你引用它的 Error
方法,但你没有调用它(调用它看起来像Error()
这将导致 string
类型的值无论如何都无法与 nil
进行比较...),因此您的 err
变量将是一个函数值,这会引起恐慌如果 Collection.Insert()
returns 一个明确的 nil
错误值。
如果你想检查某个函数 returns 是否出错,不要调用它的 error.Error()
方法,只需像这样检查 error
值本身:
if err := txnColl.Insert(txn); err != nil {
panic(err)
}
一些其他注意事项:
使用sync.WaitGroup
等待goroutines,time.Sleep()
对于demo来说可能还可以,但是对于"production"代码来说真的很糟糕。有关示例,请参阅 and 。
也在main()
末尾注册一个defer
函数到"catch"恐慌没有效果:
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
}
}()
这应该是 main()
中的第一个(或更早但绝对不是最后一个)调用。此外,如果您只是打印错误,则没有必要,因为还将打印未从中恢复的恐慌。如果您打算处理这种情况,请使用 recover()
。
同样在 readFile()
中读取文件时,您还应该检查 io.EOF
,因为文件的行数可能没有您预期的那么多,在这种情况下您可能会中断早。
我试图使用 go-client (mgo) 在 mongoDB 中插入文档。我创建了一个新的 mongo 会话,以及两个用于通信的通道 b/w go-routines,channel 用于同步b/w readFile 和 main, 同时另一种是将从 readFile 中的文件读取的数据传递给 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)
}
}()
}
然后,goroutine readFile 启动,开始从输入文件读取数据,并将数据写入 txnChannel频道。完成后通过写入channelchannel.
来标记完成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()
returns 一个 error
类型的值,你引用它的 Error
方法,但你没有调用它(调用它看起来像Error()
这将导致 string
类型的值无论如何都无法与 nil
进行比较...),因此您的 err
变量将是一个函数值,这会引起恐慌如果 Collection.Insert()
returns 一个明确的 nil
错误值。
如果你想检查某个函数 returns 是否出错,不要调用它的 error.Error()
方法,只需像这样检查 error
值本身:
if err := txnColl.Insert(txn); err != nil {
panic(err)
}
一些其他注意事项:
使用sync.WaitGroup
等待goroutines,time.Sleep()
对于demo来说可能还可以,但是对于"production"代码来说真的很糟糕。有关示例,请参阅
也在main()
末尾注册一个defer
函数到"catch"恐慌没有效果:
defer func() {
if r := recover(); r != nil {
fmt.Println(r)
}
}()
这应该是 main()
中的第一个(或更早但绝对不是最后一个)调用。此外,如果您只是打印错误,则没有必要,因为还将打印未从中恢复的恐慌。如果您打算处理这种情况,请使用 recover()
。
同样在 readFile()
中读取文件时,您还应该检查 io.EOF
,因为文件的行数可能没有您预期的那么多,在这种情况下您可能会中断早。