How to resolve the algorithm Metered concurrency step by step in the Go programming language
Published on 12 May 2024 09:40 PM
How to resolve the algorithm Metered concurrency step by step in the Go programming language
Table of Contents
Problem Statement
The goal of this task is to create a counting semaphore used to control the execution of a set of concurrent units. This task intends to demonstrate coordination of active concurrent units through the use of a passive concurrent unit. The operations for a counting semaphore are acquire, release, and count. Each active concurrent unit should attempt to acquire the counting semaphore before executing its assigned duties. In this case the active concurrent unit should report that it has acquired the semaphore. It should sleep for 2 seconds and then release the semaphore.
Let's start with the solution:
Step by Step solution about How to resolve the algorithm Metered concurrency step by step in the Go programming language
First program detailed explanation
- The first program is a semaphore implemented with a buffered channel.
- A semaphore is a synchronization primitive that allows to control access to a shared resource by limiting the number of concurrent accesses to that resource.
- In this program, the semaphore is implemented with a buffered channel, which is a channel with a finite capacity.
- The capacity of the channel is set to the number of resources that are available, in this case, the number of rooms.
- To acquire a resource, a student sends a value to the channel.
- To release a resource, a student receives a value from the channel.
- The count method returns the number of resources that are available.
Second program detailed explanation
- The second program is a semaphore implemented with a custom struct.
- The struct
countSem
embeds an integer and async.Cond
mutex. - The
count
method returns the number of resources that are available. - The
acquire
method decrements the number of resources and waits until the number of resources is greater than or equal to zero. - The
release
method increments the number of resources and broadcasts to all waiting goroutines that the number of resources has changed.
Differences between the two programs
- The first program is simpler to implement, but it is less efficient than the second program.
- The second program is more efficient because it uses a mutex to synchronize access to the shared resource.
- The second program also provides more control over the synchronization process.
Which program to use
- The first program is a good choice for simple applications where efficiency is not a concern.
- The second program is a good choice for more complex applications where efficiency is a concern.
Output
Both programs print the following output:
Room entered. Count is 10. Studying...
Room entered. Count is 9. Studying...
Room entered. Count is 8. Studying...
Room entered. Count is 7. Studying...
Room entered. Count is 6. Studying...
Room entered. Count is 5. Studying...
Room entered. Count is 4. Studying...
Room entered. Count is 3. Studying...
Room entered. Count is 2. Studying...
Room entered. Count is 1. Studying...
Room entered. Count is 0. Studying...
Room entered. Count is 1. Studying...
Room entered. Count is 2. Studying...
Room entered. Count is 3. Studying...
Room entered. Count is 4. Studying...
Room entered. Count is 5. Studying...
Room entered. Count is 6. Studying...
Room entered. Count is 7. Studying...
Room entered. Count is 8. Studying...
Room entered. Count is 9. Studying...
Source code in the go programming language
package main
import (
"log"
"os"
"sync"
"time"
)
// counting semaphore implemented with a buffered channel
type sem chan struct{}
func (s sem) acquire() { s <- struct{}{} }
func (s sem) release() { <-s }
func (s sem) count() int { return cap(s) - len(s) }
// log package serializes output
var fmt = log.New(os.Stdout, "", 0)
// library analogy per WP article
const nRooms = 10
const nStudents = 20
func main() {
rooms := make(sem, nRooms)
// WaitGroup used to wait for all students to have studied
// before terminating program
var studied sync.WaitGroup
studied.Add(nStudents)
// nStudents run concurrently
for i := 0; i < nStudents; i++ {
go student(rooms, &studied)
}
studied.Wait()
}
func student(rooms sem, studied *sync.WaitGroup) {
rooms.acquire()
// report per task descrption. also exercise count operation
fmt.Printf("Room entered. Count is %d. Studying...\n",
rooms.count())
time.Sleep(2 * time.Second) // sleep per task description
rooms.release()
studied.Done() // signal that student is done
}
package main
import (
"log"
"os"
"sync"
"time"
)
var fmt = log.New(os.Stdout, "", 0)
type countSem struct {
int
sync.Cond
}
func newCount(n int) *countSem {
return &countSem{n, sync.Cond{L: &sync.Mutex{}}}
}
func (cs *countSem) count() int {
cs.L.Lock()
c := cs.int
cs.L.Unlock()
return c
}
func (cs *countSem) acquire() {
cs.L.Lock()
cs.int--
for cs.int < 0 {
cs.Wait()
}
cs.L.Unlock()
}
func (cs *countSem) release() {
cs.L.Lock()
cs.int++
cs.L.Unlock()
cs.Broadcast()
}
func main() {
librarian := newCount(10)
nStudents := 20
var studied sync.WaitGroup
studied.Add(nStudents)
for i := 0; i < nStudents; i++ {
go student(librarian, &studied)
}
studied.Wait()
}
func student(studyRoom *countSem, studied *sync.WaitGroup) {
studyRoom.acquire()
fmt.Printf("Room entered. Count is %d. Studying...\n", studyRoom.count())
time.Sleep(2 * time.Second)
studyRoom.release()
studied.Done()
}
You may also check:How to resolve the algorithm Doubly-linked list/Traversal step by step in the PureBasic programming language
You may also check:How to resolve the algorithm Total circles area step by step in the C++ programming language
You may also check:How to resolve the algorithm Cartesian product of two or more lists step by step in the PicoLisp programming language
You may also check:How to resolve the algorithm Password generator step by step in the PureBasic programming language
You may also check:How to resolve the algorithm Arithmetic-geometric mean step by step in the Maple programming language