Ramin Mir.
Ramin Mir.

Reputation: 784

How to run a golang function call once per key parameter in concurrent environment?

This example seems to fetch data once, but I don't think it will invoke a fetchDataInBackground function in background for each key concurrently.

func Get(key string){
    ...
    if itIsTheTimeToRefreshData{
         var once sync.Once
         onceBody := func() {
            fetchDataInBackground()
         }
         go func() {
            once.Do(onceBody)
         }()
     }
     ...
 }

what I need to do is to assign each Once instance to a key so that all fetch data for different keys can be done concurrently. how do I do that?

Upvotes: 1

Views: 2705

Answers (1)

sisoft
sisoft

Reputation: 973

I think sync.Once is not well for such task. You need to maintain some flag ("once") for each key individually. Map with mutexed access is one of possible solutions.

Full working example:

package main

import (
        "fmt"
        "sync"
        "time"
)

var once map[string]bool
var onceMutex sync.Mutex
var itIsTheTimeToRefreshData = true

func Get(key string) {
        fmt.Printf("Access for key: %s\n", key)
        if itIsTheTimeToRefreshData {
                onceBody := func() {
                        fmt.Printf("Only once for key: %s\n", key)
                        //fetchDataInBackground()
                }

                onceMutex.Lock()
                if !once[key] { // is it first time?
                        once[key] = true
                        onceMutex.Unlock()

                        // refresh something here
                        go onceBody()
                } else {
                        onceMutex.Unlock()
                }
        }
}

func main() {
        once = make(map[string]bool)

        // do it first time in parallel
        for i := 0; i < 10; i++ {
                key := fmt.Sprintf("i%d", i)
                go func(key string) {
                        Get(key)
                }(key)
        }
        // and another time
        for i := 0; i < 10; i++ {
                key := fmt.Sprintf("i%d", i)
                go func(key string) {
                        Get(key)
                }(key)
        }
        fmt.Println("All requested.")
        time.Sleep(1 * time.Second)
}

Upvotes: 4

Related Questions