Cannot read from channel in a goroutine

Hello,
I wrote the following code.

package main

import (
	"time"
	"fmt"
)
var ch chan int
var ptimer map[string]*time.Timer
var m map[string]int
var startTime int64
func fn(name string, i int) {
	fmt.Println("this my name:", name)
	flag := false
	for {
		endTime := time.Now().UnixNano()
		fmt.Println("	in for duration:", float64((endTime-startTime)))
		select {
		case tmp := <-ch:
			fmt.Println("	this is ch, tmp:", tmp)
			flag = true
			break
		case <-ptimer[name].C:
			_, ok := m[name]
			if !ok{
				fmt.Println("	", name, "not in m")
				flag = true
				break
			}
			if 1==1 {
				fmt.Println("	this is in 1==1!!!!!!!!!!!!!!!!!!!!")
				ptimer[name].Reset(time.Second*20)
				break
			}
		}
		if flag == true {
			fmt.Println("	in flag == true no.:", i, "\n")
			break
		}
	}
}
func fib(a int) int {
	if a==0 {
		return 0
	}else if a==1{
		return 1
	}else {
		return fib(a-1)+fib(a-2)
	}
}
func main() {
	fmt.Println("this is in main")
	name := "hello"
	m = make(map[string]int)
	ptimer = make(map[string]*time.Timer)
	startTime := time.Now().UnixNano()

	i:=1
	for{
		m[name] = 30
		ptimer[name] = time.NewTimer(time.Second*20)
		go fn(name, i)
		fmt.Println("fib:", fib(m[name]))
		endTime := time.Now().UnixNano()
		fmt.Println("duration:", float64((endTime-startTime)))
		delete(m, name)
		ch = make(chan int, 1)
		ch <- i
		fmt.Println("after send exit_overtime\n")
		i++
		time.Sleep(time.Second)
	}
}

When I set the parameter of the fib function to 10, everything goes well.

However, when I set it to 30, something unexpected happens. I find that the first fn goroutine, which the argument i is 1, cannot receives the ch channel message.

I print some information during the execution. It shows that when the parameter is 30, it will first enter the for loop in fn function. After that , it then finishes the execution of fib function and sends i to the ch channel. In the situation that the parameter is ‘10’, it can finish fib caculation before entering the fn function. So, the first ‘fn’ goroutine can receive ‘ch’ channel and exit.

The problem is that the first ‘fn’ goroutine can only exit when the ptimer times up. If I don’t have this ptimer, the first fn goroutine can never exit?

I wonder why the execution of the for loop in fn prior to the main function send i to ch channel can make the first fn cannot receive the ch. By the way, the message first ch receive will be received in the second fn goroutine.

And I wonder is there any solution for this problem? To be more specific, under the premise of not changing the sequence, how the first fn goroutine exit when sending i to ch at the first time. The sequence is that the go fn(name, i) need to prior to fib execution.

I find a solution but I still don’t know why the execution of the for loop in fn prior to the main function send i to ch channel can make the first fn cannot receive the ch . If anyone knows, could you please explain to me? Thanks.

Hi @5151,

Welcome to the Go Forum.

Your fibonacci function is ok but I have difficulties understanding what function fn() is supposed to do, and the roles of global and local variables.
Instead of digging through the logic, let me start with the problems that seem the most obvious ones to me.

  1. Both main() and fn() access global variables concurrently. Specifically, the two maps m and ptimer. You might WILL get data races here. To address this, either guard the access with mutexes, or (maybe clearer) pass data through channels. Remember the Go proverb: “Don’t communicate by sharing memory, share memory by communicating.”

    Tip: Compile your code with the -race flag, run the code, and inspect the issued data race warnings.

  2. Function main() constantly overwrites the value in ptimer["hello"] with new timers, while the fn() goroutines try to read from that map entry. I guess that you want to have one timer per goroutine, so maybe it would be more straightforward to start that timer inside the goroutine itself.

My questions:

  1. Do the suggestions help?
  2. If they don’t help, can you reduce the code to the most minimal code that still replicates the problem?