programming

Getting to Know Sync Cond on Golang

Introduction of sync.Cond

sync.Cond is a locking process that is used with certain conditions. sync.Cond in the Golang synchronization package implements conditional variables that can be used in scenarios where multiple readers are waiting for concurrent resources. Cond pooling point: multiple goroutines waiting, 1 goroutine notification event occurs. Each Cond is associated with a Lock **(sync.Mutex or sync.RWMutex) that should be added when modifying the condition or calling the wait() method, to protect the condition.

Why should we use sync.Cond?

This condition is suitable for instructing the program that runs in the goroutine to execute it one by one. If you want to do it one by one then the signal() method is executed to activate only one goroutine. Meanwhile, if you want to activate and execute the program using all goroutines then you can use the broadcast() method.

Method Usage

  • NewCond() is used to create a condition in a variable
  • Broadcast() is used to notify all goroutines so that they don’t have to wait anymore
  • Signal() is used to notify a goroutine so that it does not have to wait anymore

Implementation and sample of sync.Cond

Look at the program that has been created below.

var locker = &sync.Mutex{}
var cond = sync.NewCond(locker)
var group = &sync.WaitGroup{}

func WaitCondition(value int) {
	cond.L.Lock()
	fmt.Println("waiting", value)
	cond.Wait()
	fmt.Println("Done", value)
	cond.L.Unlock()
	group.Done()
}

The function we have created is a condition to output the input of waitCondition where we can print the value sent to the function. Next we create the first one using the signal method. The following main function below is how to execute this sync.Cond condition.

func main() {
	for i := 0; i < 10; i++ {
		group.Add(1)
		go WaitCondition(i)
	}

	go func() {
		for i := 0; i < 10; i++ {
			time.Sleep(1 * time.Second)
			cond.Signal()
		}
	}()

	group.Wait()
}

If we run this program, the running goroutines are about 10, but they are still waiting for the condition that signal. After we call cond.Signal() on the next * goroutine*, the input sent will be printed gradually with a 1 second pause because we set sleep. The results will not be sequential because the execution will run simultaneously using the active * goroutine*.

waiting 0
waiting 5
waiting 1
waiting 2
waiting 3
waiting 4
waiting 7
waiting 6
waiting 8
waiting 9
Done 0
Done 5
Done 1
Done 2
Done 3
Done 4
Done 7
Done 6
Done 8
Done 9

Then, what if we want to activate all the goroutines at the same time so that there is no need for a 1 second pause? Then if we want that, we need to change the original call cond.Signal() to cond.Broadcast(). That method will send a signal that all goroutines can be active simultaneously. Here is the command to run all the goroutines below.

func main() {
	for i := 0; i < 10; i++ {
		group.Add(1)
		go WaitCondition(i)
	}

	go func() {
		for i := 0; i < 10; i++ {
			time.Sleep(1 * time.Second)
			cond.Broadcast()
		}
	}()

	group.Wait()
}

Conclusion

sync.Cond is an asynchronize operation that we can use for some conditions where for example we want to execute large data with some conditions so that the * goroutine* that runs is selected not all of them run because we wait for the signal which goroutine runs the command first. We can use various implementations to make the process of using goroutines more effective.

comments powered by Disqus