Story:
I have a problem when a card that previously had data but should now be empty reports len()of> 0, although it seems empty, and I have no idea why.
Longer story:
I need to process several devices at a time. Each device can have several messages. concurrency Go looked like an obvious place to start, so I wrote code to handle it, and it seems to work mostly very well. But...
I launched one channel for each device. In the function main()I have map, which contains each of the devices. When a message arrives, I check if the device exists, and if I do not create it, save it on the card, and then transfer the message to the device receiving the buffer channel.
This works great and every device handles beautifully. However, I need the device (and its goroutine) to stop when it does not receive any messages for a given period of time. I did this by checking goroutine myself, how much time has passed since the last message was received, and if goroutine is considered obsolete, then the receiving channel is closed. But how to remove from the card?
So, I passed the pointer to the card, and I have goroutine to remove the device from the card and close the receiving channel before returning. The problem is that in the end I found that the function len()returns a value> 0, but when I output the card, I see that it is empty.
I wrote a toy example to try to reproduce the error, and indeed, I see that it len()reports> 0 when the card is apparently empty. The last time I tried this, I saw 10. The time before this 14. The time before this, 53.
So, I can replicate the error, but I'm not sure if this error is to blame with me or with Go. As len()reported> 0, when there are no elements in it?
Here is an example of how I was able to replicate. I am using Go v1.5.1 windows / amd64
There are two things here:
- I manage goroutines correctly (maybe not) and
len(m) report > 0, ?
:
package main
import (
"log"
"os"
"time"
)
const (
chBuffSize = 100
thingIdleLifetime = time.Second * 5
thingsToMake = 1000
thingMessageCount = 10
)
type thing struct {
id string
ch chan bool
}
func main() {
things := make(map[string]thing)
for i := 0; i < thingsToMake; i++ {
t := thing{
id: string(i),
ch: make(chan bool, chBuffSize),
}
things[t.id] = t
go doSomething(t, &things)
go func(t thing) {
for x := 0; x < thingMessageCount; x++ {
t.ch <- true
}
}(t)
}
size := 0
for {
if size == len(things) && size != thingsToMake {
log.Println("Same number of items in map as last time")
log.Println(things)
os.Exit(1)
}
size = len(things)
log.Printf("Map size: %d\n", size)
time.Sleep(time.Second)
}
}
func doSomething(t thing, things *map[string]thing) {
lastAccessed := time.Now()
for {
select {
case <-t.ch:
lastAccessed = time.Now()
default:
n := time.Now()
d := n.Sub(lastAccessed)
if d > thingIdleLifetime {
close(t.ch)
delete(*things, string(t.id))
return
}
}
time.Sleep(time.Second)
}
}
; . TCP- , , , goroutine. , len() main(), .
2015/11/23 15:56 UTC
. , @RobNapier , . , thingsToMake , 100000, , :
goroutine 199734 [select]:
main.doSomething(0xc0d62e7680, 0x4, 0xc0d64efba0, 0xc082016240)
C:/Users/anttheknee/go/src/maptest/maptest.go:83 +0x144
created by main.main
C:/Users/anttheknee/go/src/maptest/maptest.go:46 +0x463
, , , . ?
package main
import (
"log"
"os"
"time"
)
const (
chBuffSize = 100
thingIdleLifetime = time.Second * 5
thingsToMake = 10000
thingMessageCount = 10
)
type thing struct {
id string
ch chan bool
done chan string
}
func main() {
things := make(map[string]thing)
doneCh := make(chan string, chBuffSize)
log.Printf("Making %d things\n", thingsToMake)
for i := 0; i < thingsToMake; i++ {
t := thing{
id: string(i),
ch: make(chan bool, chBuffSize),
done: doneCh,
}
things[t.id] = t
go doSomething(t)
go func(t thing) {
for x := 0; x < thingMessageCount; x++ {
t.ch <- true
time.Sleep(time.Millisecond * 10)
}
}(t)
}
log.Printf("All %d things made\n", thingsToMake)
for {
id := <-doneCh
close(things[id].ch)
delete(things, id)
if len(things) == 0 {
log.Printf("Map: %v", things)
log.Println("All done. Exiting")
os.Exit(0)
}
}
}
func doSomething(t thing) {
timer := time.NewTimer(thingIdleLifetime)
for {
select {
case <-t.ch:
timer.Reset(thingIdleLifetime)
case <-timer.C:
t.done <- t.id
return
}
}
}
2015/11/23 16:41 UTC
, . , , - , , ( , , !)
package main
import (
"log"
"os"
"strconv"
"time"
)
const (
chBuffSize = 100
thingIdleLifetime = time.Second * 5
thingsToMake = 100000
thingMessageCount = 10
)
type thing struct {
id string
receiver chan bool
done chan string
}
func main() {
things := make(map[string]thing)
doneCh := make(chan string, chBuffSize)
log.Printf("Making %d things\n", thingsToMake)
for i := 0; i < thingsToMake; i++ {
t := thing{
id: strconv.Itoa(i),
receiver: make(chan bool, chBuffSize),
done: doneCh,
}
things[t.id] = t
go doSomething(t)
go func(t thing) {
for x := 0; x < thingMessageCount; x++ {
t.receiver <- true
time.Sleep(time.Millisecond * 100)
}
}(t)
}
log.Printf("All %d things made\n", thingsToMake)
go func() {
for {
time.Sleep(time.Second)
m := things
log.Printf("Map length: %v", len(m))
if len(m) == 0 {
log.Printf("Confirming empty map: %v", things)
log.Println("All done. Exiting")
os.Exit(0)
}
}
}()
for {
id := <-doneCh
close(things[id].receiver)
delete(things, id)
}
}
func doSomething(t thing) {
timer := time.NewTimer(thingIdleLifetime)
for {
select {
case <-t.receiver:
timer.Reset(thingIdleLifetime)
case <-timer.C:
t.done <- t.id
return
}
}
}