Metered concurrency: Difference between revisions

Content added Content deleted
(→‎Go, Buffered channel: small improvement to code, more explanation.)
Line 606: Line 606:
=={{header|Go}}==
=={{header|Go}}==
===Buffered channel===
===Buffered channel===
Recommended solution for simplicity. Acquire operation is channel send, release is channel receive, and count is provided with len(channel). Also WaitGroup used as a completion checkpoint.
Recommended solution for simplicity. Acquire operation is channel send, release is channel receive, and count is provided with len(channel).

To demonstrate, this example implements the [https://en.wikipedia.org/wiki/Semaphore_(programming)#Library_analogy Library analogy] from Wikipedia with 10 study rooms and 20 students.

The channel type shown here is <code>struct{}</code>. <code>struct{}</code> is nice because it has zero size and zero content, although the syntax is slightly akward. Other popular choices for no-content tokens are ints and bools. They read a little nicer but waste a few bytes and could potentially mislead someone to think the values had some meaning.

A couple of other concurrency related details used in the example are the log package for serializing output and sync.WaitGroup used as a completion checkpoint. Functions of the fmt package are not synchronized and can produce interleaved output with concurrent writers. The log package does nice synchronization to avoid this.
<lang go>package main
<lang go>package main

import (
import (
"log"
"log"
Line 615: Line 621:
"time"
"time"
)
)

// log package serializes output
// log package serializes output
var fmt = log.New(os.Stdout, "", 0)
var fmt = log.New(os.Stdout, "", 0)

// library analogy per WP article
// library analogy per WP article
const nRooms = 10
const nRooms = 10
const nStudents = 20
const nStudents = 20

func main() {
func main() {
// buffered channel used as a counting semaphore
// buffered channel used as a counting semaphore
rooms := make(chan int, nRooms)
rooms := make(chan struct{}, nRooms)
for i := 0; i < nRooms; i++ {
for i := 0; i < nRooms; i++ {
rooms <- 1
rooms <- struct{}{}
}
}
// WaitGroup used to wait for all students to have studied
// WaitGroup used to wait for all students to have studied
Line 639: Line 645:
studied.Wait()
studied.Wait()
}
}

func student(rooms chan int, studied *sync.WaitGroup) {
func student(rooms chan struct{}, studied *sync.WaitGroup) {
<-rooms // acquire operation
<-rooms // acquire operation
// report per task descrption. also exercise count operation
// report per task descrption. also exercise count operation
fmt.Printf("Room entered. Count is %d. Studying...\n",
fmt.Printf("Room entered. Count is %d. Studying...\n",
len(rooms)) // len function provides count operation
len(rooms)) // len function provides count operation
time.Sleep(2 * time.Second) // sleep per task description
time.Sleep(2 * time.Second) // sleep per task description
rooms <- 1 // release operation
rooms <- struct{}{} // release operation
studied.Done() // signal that student is done
studied.Done() // signal that student is done
}</lang>
}</lang>
Output for this and the other Go programs here shows 10 students studying immediately, about a 2 second pause, 10 more students studying, then another pause of about 2 seconds before returning to the command prompt.
Output for this and the other Go programs here shows 10 students studying immediately, about a 2 second pause, 10 more students studying, then another pause of about 2 seconds before returning to the command prompt. In this example the count values may look jumbled. This is a result of the student goroutines running concurrently.


===Sync.Cond===
===Sync.Cond===