Accessing a nil key in a map concurrently

I have two for loops that are running in their own goroutine and they check different keys in a map many times. One of the for loops use a ticker for 1 second intervals so that is why I have to wait for time.Sleep(time.Second *2) instead of 1 second or less.

For example of one goroutine would be:

go func(){

     timer1 := time.NewTicker(time.Second)

    for _ = range timer1.C{
         if randomMap("someKey")=="test" ||  randomMap("somethingElse")=="blah" {
                 fmt.Println("Hello world")
         }
         if some condition is met{
               return
         {
    }
}()

While this goroutine is running I delete the someKey from the randomMap and it gives an error. I cannot check if the key exist because the very act of checking the map to see if a key exist gives a runtime error.

The only solution I was able to work around was to implement an extra if statement in the for loop to check if a condition indicated whether or the key from randomMap was deleted. So before I deleted the key in randomMap I would first change a conditional variable. Then I had to use time.Sleep(time.Second * 2) in order to give enough time for the goroutines to hit the correct conditional statement indicating the key in randomMap was deleted.

For example:

 func someFunctiontoDeleteMapKey(){
        
    randomMap.isDeleted = true
    time.Sleep(time.Second * 2)   
    delete(randomMap, "someKey")
}
    func someOtherFunctionInADifferentFile(){
         go func(){
               timer1 := time.NewTicker(time.Second)
        
               for _ = range timer1.C{
                      if randomMap.isDeleted == false{
                                if randomMap("someKey")=="test" ||  randomMap("somethingElse")=="blah" {
                                      fmt.Println("Hello world")
                                }
                                if some condition is met{
                                             return
                               }
                        }else{
                                  return
                        }         
             }
      }()
    
    
    
    }    

It feels clumsy to have to call time.Sleep. Is there a way I could use channels to “wait” for the goroutine until it hits return?

Edit: Why is tabs disabled in the editor? So painful.

It’s not safe to read and write a map concurrently. (Deleting a key is a type of writing).

You should probably be communicating with channels, or at the least, guarding your map with a mutex.

1 Like

How do I go about doing this? I tried doing it but somehow it didn’t work. My functions are spread across in different files/scopes. I tried to use the someChannel := make(chan bool, 1) and
<- someChannel which will cause the goroutine to wait at that point until a boolean is passed to someChannel but when I do pass a boolean the someChannel does not proceed and it gets stucked there. I think it has to do because of scope issue.

I tried different ways such as moving the someChannel := make(chan bool, 1) to out a outer scope or passing in the someChannel through the goroutine function as a parameter but my program would still be stuck when it hit the <-someChannel. My program compiles fine and no errors are given during run time so I don’t know what I am doing wrong.

To give you an example of what I am trying to do I am trying to copy the technique at this page here:
https://gobyexample.com/channel-synchronization

See how the <- done is holding off the program at that point until true is passed to the done channel. There are working in two different functions so their scope is not the same which is what I am trying to reproduce but on a much larger scale.

Concurrent access of a map:

type S struct {
   m map[string]int64
   sync sync.Mutex
}
func (s *S) concurrentAccess(k string) int64 {
    s.sync.Lock()
    defer s.sync.Unlock()
    return s.m[k]
}

Sometimes you want a channel, other times you just want to guard your values. Which you want depends on the overall program flow.

Access a nil map key?
http://play.golang.org/p/PQlQyOb6EW

Yes, this is what I need in theory. Now I am trying to currently implement your idea in my code.
I originally made a global map so I only want one copy of it and every times its accessed/modified its the same original copy. This implementation is not good as its giving me these nil map errors.

So my question regarding the code you posted, is the m map[string]int64 a copy when its passed as s *S or is the actual map being passed in(no copy being made)?

I see the pointer in the method receiver s *S so I am assuming its passing the address and no copies are being made. But I am not certain.

Edit: okay so the golang blog says to make concurrent maps like, I guess thats how should I do it:
var counter = struct{
sync.RWMutex
m map[string]int
}{m: make(map[string]int)}

Sure. Just be sure to call counter.Lock() and counter.Unlock() around any counter.m[X] code.

*S is of type pointer-to-S-type. So it is passing a pointer to a location. When s.property is modified, only a single instance is modified, the instance *S points to.

1 Like

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.