NOTE

2.10 sync.Cond

What sync.Cond is, Wait/Broadcast/Signal semantics, and a blocking-queue example using condition variables.

GoCreated Updated 1 min readhistorical

This is a historical learning note and may contain outdated or incomplete understanding.

1. What It Is

  • A condition variable. The relationship between Locker and Cond is similar to the relationship between Lock and Condition in Java.
  • All methods must be called while holding the Lock.
  • func NewCond(l Locker) *Cond creates a Cond using a Lock.
  • func (c *Cond) Wait() automatically releases the Lock and pauses the current goroutine, blocking until it is awakened by Broadcast or Signal. It must be executed inside a while loop.
  • func (c *Cond) Broadcast() wakes all goroutines blocked on the Cond.
  • func (c *Cond) Signal() wakes any one goroutine blocked on the Cond.

2. Usage

type BlockingQueue struct {
	lock     sync.Locker
	notFull  *sync.Cond
	notEmpty *sync.Cond
	capacity int
	items    []string
}

func (c *BlockingQueue) Add(data string) {
	c.lock.Lock()
	defer c.lock.Unlock()

	for len(c.items) == c.capacity {
		c.notFull.Wait()
	}

	c.items = append(c.items, data)
	c.notEmpty.Broadcast()
}

func (c *BlockingQueue) Remove() string {
	c.lock.Lock()
	defer c.lock.Unlock()

	for len(c.items) == 0 {
		c.notEmpty.Wait()
	}

	data := c.items[0]
	c.items = c.items[1:]
	c.notFull.Broadcast()
	return data
}
func TestConcurrentList(t *testing.T) {
	var mutex sync.Mutex
	list := &BlockingQueue{
		lock:     &mutex,
		notFull:  sync.NewCond(&mutex),
		notEmpty: sync.NewCond(&mutex),
		capacity: 10,
		items:    make([]string, 0),
	}

	var wg sync.WaitGroup
	wg.Add(2)
	go func() {
		defer wg.Done()
		for i := 0; i < 1000; i++ {
			list.Add(strconv.Itoa(i))
		}
	}()

	go func() {
		defer wg.Done()
		for {
			data := list.Remove()
			fmt.Println(data)
			if data == "999" {
				break
			}
		}
	}()

	wg.Wait()
}

Discussion

Sign in with GitHub to comment. Discussions are stored as GitHub Issues.View on GitHub