2018-01-16 04:32:21 +03:00
|
|
|
|
package main
|
|
|
|
|
|
2018-01-17 06:58:27 +03:00
|
|
|
|
import (
|
|
|
|
|
"bytes"
|
|
|
|
|
"fmt"
|
2020-05-11 13:11:18 +03:00
|
|
|
|
"math/rand"
|
2018-01-17 06:58:27 +03:00
|
|
|
|
"sync"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
2018-01-16 04:32:21 +03:00
|
|
|
|
|
|
|
|
|
// Confinement is the simple yet powerful idea of ensuring information is only ever available from one concurrent process.
|
|
|
|
|
// There are two kinds of confinement possible: ad hoc and lexical.
|
|
|
|
|
|
|
|
|
|
// Lexical confinement involves using lexical scope to expose only the correct data and
|
|
|
|
|
// concurrency primitives for multiple concurrent processes to use. It makes it impossible to do the wrong thing.
|
|
|
|
|
|
2018-01-17 06:58:27 +03:00
|
|
|
|
// If a goroutine is responsible for creating a goroutine, it is also responsible for ensuring it can stop the goroutine.
|
|
|
|
|
|
2018-01-16 04:32:21 +03:00
|
|
|
|
func main() {
|
2018-01-17 06:58:27 +03:00
|
|
|
|
// lexicalNotConcurrentSafe()
|
|
|
|
|
// lexicalDemo()
|
|
|
|
|
// blockOnAttemptingToWriteToChannel()
|
|
|
|
|
fixBlockOnAttemptingToWriteToChannel()
|
2018-01-16 04:32:21 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func lexicalDemo() {
|
|
|
|
|
// Here we instantiate the channel within the lexical scope of the chanOwner function.
|
|
|
|
|
// This limits the scope of the write aspect of the results channel to the closure
|
|
|
|
|
// defined below it. In other words, it confines the write aspect of this channel to
|
|
|
|
|
// prevent other goroutines from writing to it.
|
|
|
|
|
chanOwner := func() <-chan int {
|
|
|
|
|
results := make(chan int, 5)
|
|
|
|
|
go func() {
|
|
|
|
|
defer close(results)
|
|
|
|
|
for i := 0; i <= 5; i++ {
|
|
|
|
|
results <- i
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
return results
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Here we receive a read-only copy of an int channel. By declaring that the only
|
|
|
|
|
// usage we require is read access, we confine usage of the channel within the consume function to only reads
|
|
|
|
|
comsumer := func(results <-chan int) {
|
|
|
|
|
for result := range results {
|
|
|
|
|
fmt.Println("Received: %d\n", result)
|
|
|
|
|
}
|
|
|
|
|
fmt.Println("Done Receiving!")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Here we receive the read aspect of the channel and we’re able to pass it into the
|
|
|
|
|
// consumer, which can do nothing but read from it. Once again this confines the
|
|
|
|
|
// main goroutine to a read-only view of the channel.
|
|
|
|
|
results := chanOwner()
|
|
|
|
|
comsumer(results)
|
|
|
|
|
}
|
2018-01-17 06:58:27 +03:00
|
|
|
|
|
|
|
|
|
func lexicalNotConcurrentSafe() {
|
|
|
|
|
printData := func(wg *sync.WaitGroup, data []byte) {
|
|
|
|
|
defer wg.Done()
|
|
|
|
|
var buff bytes.Buffer
|
|
|
|
|
for _, b := range data {
|
|
|
|
|
fmt.Fprintf(&buff, "%c", b)
|
|
|
|
|
}
|
|
|
|
|
fmt.Println(buff.String())
|
|
|
|
|
}
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
wg.Add(2)
|
|
|
|
|
data := []byte("golang")
|
|
|
|
|
// Here we pass in a slice containing the first three bytes in the data structure.
|
|
|
|
|
go printData(&wg, data[:3])
|
|
|
|
|
// Here we pass in a slice containing the last three bytes in the data structure.
|
|
|
|
|
go printData(&wg, data[3:])
|
|
|
|
|
wg.Wait()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func blockOnAttemptingToWriteToChannel() {
|
|
|
|
|
newRandStream := func() <-chan int {
|
|
|
|
|
randStream := make(chan int)
|
|
|
|
|
go func() {
|
|
|
|
|
defer fmt.Println("newRandStream closure existed.")
|
|
|
|
|
defer close(randStream)
|
|
|
|
|
for {
|
|
|
|
|
randStream <- rand.Int()
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
return randStream
|
|
|
|
|
}
|
|
|
|
|
randStream := newRandStream()
|
|
|
|
|
fmt.Println("3 random ints:")
|
|
|
|
|
for i := 1; i <= 3; i++ {
|
|
|
|
|
fmt.Printf("%d: %d\n", i, <-randStream)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// The solution, just like for the receiving case, is to provide the
|
|
|
|
|
// producer goroutine with a channel informing it to exit
|
|
|
|
|
func fixBlockOnAttemptingToWriteToChannel() {
|
|
|
|
|
d := make(chan interface{})
|
|
|
|
|
newRandStream := func(done <-chan interface{}) <-chan int {
|
|
|
|
|
randStream := make(chan int)
|
|
|
|
|
go func() {
|
|
|
|
|
defer fmt.Println("newRandStream closure existed.")
|
|
|
|
|
defer close(randStream)
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
case randStream <- rand.Int():
|
|
|
|
|
case <-done:
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}()
|
|
|
|
|
return randStream
|
|
|
|
|
}
|
|
|
|
|
randStream := newRandStream(d)
|
|
|
|
|
fmt.Println("3 random ints:")
|
|
|
|
|
for i := 1; i <= 3; i++ {
|
|
|
|
|
fmt.Printf("%d: %d\n", i, <-randStream)
|
|
|
|
|
}
|
|
|
|
|
close(d)
|
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
|
}
|