diff --git a/README.md b/README.md index 7d9f5f7..d621053 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ A curated collection of idiomatic design & application patterns for Go language. | Pattern | Description | Status | |:-------:|:----------- |:------:| | [N-Barrier](/concurrency/barrier.md) | Prevents a process from proceeding until all N processes reach to the barrier | ✘ | -| [Bounded Parallelism](/concurrency/bounded_parallelism.md) | Completes large number of independent tasks with resource limits | ✔ | +| [Bounded Parallelism](/concurrency/bounded/bounded_parallelism.md) | Completes large number of independent tasks with resource limits | ✔ | | [Broadcast](/concurrency/broadcast.md) | Transfers a message to all recipients simultaneously | ✘ | | [Coroutines](/concurrency/coroutine.md) | Subroutines that allow suspending and resuming execution at certain locations | ✘ | | [Generators](/concurrency/generator.md) | Yields a sequence of values one at a time | ✔ | diff --git a/SUMMARY.md b/SUMMARY.md index 9337c64..61e09c1 100644 --- a/SUMMARY.md +++ b/SUMMARY.md @@ -33,7 +33,7 @@ * [Semaphore](/synchronization/semaphore.md) * [Concurrency Patterns](/README.md#concurrency-patterns) * [N-Barrier](/concurrency/barrier.md) - * [Bounded Parallelism](/concurrency/bounded_parallelism.md) + * [Bounded Parallelism](/concurrency/bounded/bounded_parallelism.md) * [Broadcast](/concurrency/broadcast.md) * [Coroutines](/concurrency/coroutine.md) * [Generators](/concurrency/generator.md) diff --git a/behavioral/observer/main.go b/behavioral/observer/main.go index d6fbc9f..a6a3a65 100644 --- a/behavioral/observer/main.go +++ b/behavioral/observer/main.go @@ -40,11 +40,11 @@ type ( ) type ( - eventObserver struct{ + eventObserver struct { id int } - eventNotifier struct{ + eventNotifier struct { // Using a map with an empty struct allows us to keep the observers // unique while still keeping memory usage relatively low. observers map[Observer]struct{} @@ -84,10 +84,10 @@ func main() { tick := time.NewTicker(time.Second).C for { select { - case <- stop: + case <-stop: return case t := <-tick: n.Notify(Event{Data: t.UnixNano()}) } } -} \ No newline at end of file +} diff --git a/concurrency/bounded_parallelism.go b/concurrency/bounded/bounded_parallelism.go similarity index 100% rename from concurrency/bounded_parallelism.go rename to concurrency/bounded/bounded_parallelism.go diff --git a/concurrency/bounded_parallelism.md b/concurrency/bounded/bounded_parallelism.md similarity index 71% rename from concurrency/bounded_parallelism.md rename to concurrency/bounded/bounded_parallelism.md index 8f1429c..759485b 100644 --- a/concurrency/bounded_parallelism.md +++ b/concurrency/bounded/bounded_parallelism.md @@ -1,6 +1,6 @@ # Bounded Parallelism Pattern -[Bounded parallelism](https://blog.golang.org/pipelines#TOC_9.) is similar to [parallelism](parallelism.md), but allows limits to be placed on allocation. +[Bounded parallelism](https://blog.golang.org/pipelines#TOC_9.) is similar to [parallelism](../parallelism.md), but allows limits to be placed on allocation. # Implementation and Example diff --git a/playground/goroutine/basic_chan_test.go b/playground/goroutine/basic_chan_test.go new file mode 100644 index 0000000..7403897 --- /dev/null +++ b/playground/goroutine/basic_chan_test.go @@ -0,0 +1,24 @@ +package goroutine + +import ( + "fmt" + "testing" + "time" +) + +func TestFiveGopherWithChan(t *testing.T) { + c := make(chan int) + for i := 0; i < 5; i++ { + go sleepyGopherWithChan(i, c) + } + for i := 0; i < 5; i++ { + gopherID := <-c + fmt.Println("gopher ", gopherID, " has finished sleeping") + } +} + +func sleepyGopherWithChan(id int, c chan int) { + time.Sleep(2 * time.Second) + fmt.Println("... ", id, " snore ...") + c <- id +} diff --git a/playground/goroutine/basic_test.go b/playground/goroutine/basic_test.go new file mode 100644 index 0000000..02b2347 --- /dev/null +++ b/playground/goroutine/basic_test.go @@ -0,0 +1,25 @@ +package goroutine + +import ( + "fmt" + "testing" + "time" +) + +func TestOneGopher(t *testing.T) { + go sleepyGopher(1) + time.Sleep(3 * time.Second) +} + +func TestFiveGopher(t *testing.T) { + c := make(chan int) + for i := 0; i < 5; i++ { + go sleepyGopherWithChan(i, c) + } + time.Sleep(3 * time.Second) +} + +func sleepyGopher(id int) { + time.Sleep(2 * time.Second) + fmt.Println("... ", id, " snore ...") +} diff --git a/playground/goroutine/basic_waitgroup_test.go b/playground/goroutine/basic_waitgroup_test.go new file mode 100644 index 0000000..9eab546 --- /dev/null +++ b/playground/goroutine/basic_waitgroup_test.go @@ -0,0 +1,32 @@ +package goroutine + +import ( + "fmt" + "sync" + "testing" + "time" +) + +func TestWaitGophers(t *testing.T) { + var gophers = []string{"tom", "peter", "john", "brown"} + + var waitGroup sync.WaitGroup + waitGroup.Add(len(gophers)) + + for i := 0; i < len(gophers); i++ { + go func(wg *sync.WaitGroup, name string) { + sleepyGopherSnore(name) + wg.Done() + }(&waitGroup, gophers[i]) + } + + waitGroup.Wait() + + fmt.Println("All done") +} + +func sleepyGopherSnore(name string) { + fmt.Println(name, ": ... start snore") + time.Sleep(2 * time.Second) + fmt.Println(name, ": ... snore") +} diff --git a/playground/goroutine/deadlock_test.go b/playground/goroutine/deadlock_test.go new file mode 100644 index 0000000..f5ff192 --- /dev/null +++ b/playground/goroutine/deadlock_test.go @@ -0,0 +1,11 @@ +package goroutine + +import "testing" + +// A deadlock happens when a group of goroutines are waiting for each other and none of them is able to proceed. +// The program will get stuck on the channel send operation waiting forever for someone to read the value. +// Go is able to detect situations like this at runtime. +func TestDeadlock(t *testing.T) { + c := make(chan int) + <-c +} diff --git a/playground/goroutine/pipeline_test.go b/playground/goroutine/pipeline_test.go new file mode 100644 index 0000000..4a1bb00 --- /dev/null +++ b/playground/goroutine/pipeline_test.go @@ -0,0 +1,39 @@ +package goroutine + +import ( + "fmt" + "strings" + "testing" +) + +func sourceGopher(downstream chan string) { + for _, v := range []string{"hello world", "a bad apple", "goodbye all"} { + downstream <- v + } + close(downstream) +} + +func filterGopher(upstream, downstream chan string) { + for item := range upstream { + fmt.Println("filter ", item) + + if !strings.Contains(item, "bad") { + downstream <- item + } + } +} + +func printGopher(upstream chan string) { + for item := range upstream { + fmt.Println("print ", item) + } +} + +// Pipeline source->filter->print +func TestPipeline(t *testing.T) { + c0 := make(chan string) + c1 := make(chan string) + go sourceGopher(c0) + go filterGopher(c0, c1) + printGopher(c1) +}