I am new to Golang. I have been using GORM and concurrency of go to read a SQLite database and write it into a CSV file. It is working smooth but when the processing is done it is not ending the main program and exiting. I have to print command+c
to exit. I don't know what I am doing wrong. May be it is entering into some blocking or deadlock mode or something. Moreover it is not printing a bye message too. Which means it is still trying to read the data from the channel. Please help. Here is the code.
package main
import (
"fmt"
"reflect"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
type AirQuality struct {
// gorm.Model
// ID uint `gorm:"column:id"`
Index string `gorm:"column:index"`
BEN string `gorm:"column:BEN"`
CH4 string `gorm:"column:CH4"`
CO string `gorm:"column:CO"`
EBE string `gorm:"column:EBE"`
MXY string `gorm:"column:MXY"`
NMHC string `gorm:"column:NMHC"`
NO string `gorm:"column:NO"`
NO2 string `gorm:"column:NO_2"`
NOX string `gorm:"column:NOx"`
OXY string `gorm:"column:OXY"`
O3 string `gorm:"column:O_3"`
PM10 string `gorm:"column:PM10"`
PM25 string `gorm:"column:PM25"`
PXY string `gorm:"column:PXY"`
SO2 string `gorm:"column:SO_2"`
TCH string `gorm:"column:TCH"`
TOL string `gorm:"column:TOL"`
Time string `gorm:"column:date; type:timestamp"`
Station string `gorm:"column:station"`
}
func (AirQuality) TableName() string {
return "AQ"
}
func main() {
c := generateRows("boring!!")
for {
fmt.Println(<-c)
if c == nil {
fmt.Println("Bye")
break
}
}
}
func generateRows(msg string) <-chan []string {
c := make(chan []string)
go func() {
db, err := gorm.Open("sqlite3", "./load_testing_7.6m.db")
if err != nil {
panic("failed to connect database")
}
defer db.Close()
rows, err := db.Model(&AirQuality{}).Limit(20).Rows()
defer rows.Close()
if err != nil {
panic(err)
}
for rows.Next() {
var aq AirQuality
db.ScanRows(rows, &aq)
v := reflect.Indirect(reflect.ValueOf(aq))
var buf []string
for i := 0; i < v.NumField(); i++ {
buf = append(buf, v.Field(i).String())
}
c <- buf
}
}()
return c
}
Receiving from an unbuffered channel (such as yours) where no one is ready to send a value blocks. This is what you experience. Spec: Receive operator:
The expression [
<-c
] blocks until a value is available.
The common way to signal "EOF" in case of channels is to close the channel from the sender's side when there are no more values to send, using the builtin close()
function.
Attempting to receive from a closed channel can proceed immediately, yielding the zero value of the element type of the channel. To detect this "closed" state, use the special comma-ok idiom:
value, ok := <- c
If the channel is closed, ok
will be false
(otherwise it's true
).
The easy and proper way to "drain" a channel until it is closed is to use the for range
loop, like this:
for value := range c {
fmt.Println("Received:", value)
}
The for range
terminates once all values have been received from channel c
that were sent on it before it was closed.
So inside generateRows()
, do this:
go func() {
// // Use defer so it will be closed no matter how this function call ends
defer close(c)
// ...
}()
And your main()
:
func main() {
c := generateRows("boring!!")
for v := range c {
fmt.Println(v)
}
fmt.Println("Bye")
}