{"id":16915011,"url":"https://github.com/butuzov/harmony","last_synced_at":"2025-03-22T10:32:31.216Z","repository":{"id":44660153,"uuid":"441155864","full_name":"butuzov/harmony","owner":"butuzov","description":"Go's concurrency patterns as generic library to use.","archived":false,"fork":false,"pushed_at":"2022-02-01T15:48:08.000Z","size":90,"stargazers_count":37,"open_issues_count":4,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-03-18T10:45:41.311Z","etag":null,"topics":["concurrency","concurrency-patterns","generics","go"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/butuzov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-12-23T11:16:04.000Z","updated_at":"2024-11-29T07:56:08.000Z","dependencies_parsed_at":"2022-09-26T21:50:42.207Z","dependency_job_id":null,"html_url":"https://github.com/butuzov/harmony","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/butuzov%2Fharmony","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/butuzov%2Fharmony/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/butuzov%2Fharmony/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/butuzov%2Fharmony/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/butuzov","download_url":"https://codeload.github.com/butuzov/harmony/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244944353,"owners_count":20536290,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["concurrency","concurrency-patterns","generics","go"],"created_at":"2024-10-13T19:15:58.262Z","updated_at":"2025-03-22T10:32:30.979Z","avatar_url":"https://github.com/butuzov.png","language":"Go","funding_links":[],"categories":["Go"],"sub_categories":[],"readme":"# harmony  [![Coverage Status](https://coveralls.io/repos/github/butuzov/harmony/badge.svg?t=1njyDt)](https://coveralls.io/github/butuzov/harmony) [![build status](https://github.com/butuzov/harmony/actions/workflows/main.yaml/badge.svg?branch=main)]() [![MIT License](http://img.shields.io/badge/license-MIT-blue.svg)](http://www.opensource.org/licenses/MIT)\n\nGeneric Concurrency Patterns Library\n\n## Reference (generated by [gomarkdoc](\u003chttps://github.com/princjef/gomarkdoc\u003e))\n\n\u003c!-- You can Edit Content above this comment ---\u003e\n\u003c!-- Start ---\u003e\n```go\nimport \"github.com/butuzov/harmony\"\n```\n\nPackage `harmony` provides generic concurrency patterns library, created for educational proposes by it's author. It provides next patterns: \n- `Bridge` \n- `FanIn` \n- `Feature` \n- `OrDone` / `OrWithDone` / `OrWithContext` \n- `Pipeline` \n- `Queue` \n- `Tee` \n- `WorkerPool`\n\n\u003cdetails\u003e\u003csummary\u003eExample (Fastest Sqrt)\u003c/summary\u003e\n\u003cp\u003e\n\nWhat SQRT funtion is faster? Complex example that shows the combination of few patterns Queue, Tee, FanIn patterns.\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/butuzov/harmony\"\n\t\"log\"\n\t\"math/rand\"\n\t\"time\"\n)\n\nfunc main() {\n\t// the fastert square root cracker....\n\ttype Report struct {\n\t\tMethod string\n\t\tValue  uint64\n\t}\n\n\tvar (\n\t\t// Babylonian method\n\t\tsqrtBabylonian = func(n uint64) Report {\n\t\t\tvar (\n\t\t\t\to = float64(n) // Original value as float64\n\t\t\t\tx = float64(n) // x of binary search\n\t\t\t\ty = 1.0        // y of binary search\n\t\t\t\te = 1e-5       // error\n\t\t\t)\n\n\t\t\tfor x-y \u003e e {\n\t\t\t\tx = (x + y) / 2\n\t\t\t\ty = o / x\n\t\t\t\t// fmt.Printf(\"y=%f, x=%f.\", y, x)\n\t\t\t}\n\n\t\t\treturn Report{\"Babylonian\", uint64(x)}\n\t\t}\n\n\t\t// Bakhshali method\n\t\tsqrtBakhshali = func(n uint64) Report {\n\t\t\titerate := func(x float64) float64 {\n\t\t\t\ta := (float64(n) - x*x) / (2 * x)\n\t\t\t\txa := x + a\n\t\t\t\treturn xa - ((a * a) / (2 * xa))\n\t\t\t}\n\n\t\t\tvar (\n\t\t\t\to = float64(n)\n\t\t\t\tx = float64(n) / 2.0\n\t\t\t\te = 1e-5\n\t\t\t)\n\n\t\t\tfor x*x-o \u003e e {\n\t\t\t\tx = iterate(x)\n\t\t\t}\n\t\t\treturn Report{\"Bakhshali\", uint64(x)}\n\t\t}\n\t)\n\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\tch, _ := harmony.FututeWithContext(ctx, func() uint64 {\n\t\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\t\tv := r.Uint64()\n\t\tfmt.Printf(\"Initial number: %d.\", v)\n\t\treturn v\n\t})\n\n\tif ch1, ch2, err := harmony.TeeWithContext(ctx, ch); err == nil {\n\t\tlog.Printf(\"err: %v\", err)\n\t\treturn\n\t} else {\n\t\tchRep1, _ := harmony.PipelineWithContext(ctx, ch1, 1, sqrtBabylonian)\n\t\tchRep2, _ := harmony.PipelineWithContext(ctx, ch2, 1, sqrtBakhshali)\n\n\t\tchRep1, _ = harmony.OrDoneWithContext(ctx, chRep1)\n\t\tchRep2, _ = harmony.OrDoneWithContext(ctx, chRep2)\n\n\t\tout, _ := harmony.FanInWithContext(ctx, chRep1, chRep2)\n\t\tfmt.Printf(\"Result is :%v\", \u003c-out)\n\t}\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n### Index\n\n- [Variables](\u003c#variables\u003e)\n- [func Bridge[T any](done \u003c-chan struct{}, incoming \u003c-chan (\u003c-chan T)) (\u003c-chan T, error)](\u003c#func-bridge\u003e)\n- [func BridgeWithContext[T any](ctx context.Context, incoming \u003c-chan (\u003c-chan T)) (\u003c-chan T, error)](\u003c#func-bridgewithcontext\u003e)\n- [func FanIn[T any](done \u003c-chan struct{}, ch1, ch2 \u003c-chan T, channels ...\u003c-chan T) (\u003c-chan T, error)](\u003c#func-fanin\u003e)\n- [func FanInWithContext[T any](ctx context.Context, ch1, ch2 \u003c-chan T, channels ...\u003c-chan T) (\u003c-chan T, error)](\u003c#func-faninwithcontext\u003e)\n- [func Futute[T any](done \u003c-chan struct{}, futureFn func() T) (\u003c-chan T, error)](\u003c#func-futute\u003e)\n- [func FututeWithContext[T any](ctx context.Context, futureFn func() T) (\u003c-chan T, error)](\u003c#func-fututewithcontext\u003e)\n- [func OrDone[T any](done \u003c-chan struct{}, incoming \u003c-chan T) (\u003c-chan T, error)](\u003c#func-ordone\u003e)\n- [func OrDoneWithContext[T any](ctx context.Context, incoming \u003c-chan T) (\u003c-chan T, error)](\u003c#func-ordonewithcontext\u003e)\n- [func Pipeline[T1, T2 any](done \u003c-chan struct{}, incomingCh \u003c-chan T1, totalWorkers int, workerFn func(T1) T2) (\u003c-chan T2, error)](\u003c#func-pipeline\u003e)\n- [func PipelineWithContext[T1, T2 any](ctx context.Context, incomingCh \u003c-chan T1, totalWorkers int, workerFn func(T1) T2) (\u003c-chan T2, error)](\u003c#func-pipelinewithcontext\u003e)\n- [func Queue[T any](done \u003c-chan struct{}, genFn func() T) (\u003c-chan T, error)](\u003c#func-queue\u003e)\n- [func QueueWithContext[T any](ctx context.Context, genFn func() T) (\u003c-chan T, error)](\u003c#func-queuewithcontext\u003e)\n- [func Tee[T any](done \u003c-chan struct{}, incoming \u003c-chan T) (\u003c-chan T, \u003c-chan T, error)](\u003c#func-tee\u003e)\n- [func TeeWithContext[T any](ctx context.Context, incoming \u003c-chan T) (\u003c-chan T, \u003c-chan T, error)](\u003c#func-teewithcontext\u003e)\n- [func WorkerPool[T any](done \u003c-chan struct{}, jobQueue chan T, maxWorkers int, workFunc func(T)) error](\u003c#func-workerpool\u003e)\n- [func WorkerPoolWithContext[T any](ctx context.Context, jobQueue chan T, maxWorkers int, workFunc func(T)) error](\u003c#func-workerpoolwithcontext\u003e)\n\n\n### Variables\n\n```go\nvar ErrContext = errors.New(\"harmony: nil Context\")\n```\n\n```go\nvar ErrDone = errors.New(\"harmony: nil done chant\")\n```\n\n### func Bridge\n\n```go\nfunc Bridge[T any](done \u003c-chan struct{}, incoming \u003c-chan (\u003c-chan T)) (\u003c-chan T, error)\n```\n\nBridge will return chan of generic type `T` used a pipe for the values received from the sequence of channels or `ErrDone`. Close received channel .one you got from`incoming`. in order to switch for a new one. Goroutines exists on close of `incoming` or done chan closed.\n\n### func BridgeWithContext\n\n```go\nfunc BridgeWithContext[T any](ctx context.Context, incoming \u003c-chan (\u003c-chan T)) (\u003c-chan T, error)\n```\n\nBridgeWithContext will return chan of generic type `T` used a pipe for the values received from the sequence of channels or `ErrContext`. Close received channel .one you got from`incoming`. in order to switch for a new one. Goroutines exists on close of `incoming` or context canceled.\n\n### func FanIn\n\n```go\nfunc FanIn[T any](done \u003c-chan struct{}, ch1, ch2 \u003c-chan T, channels ...\u003c-chan T) (\u003c-chan T, error)\n```\n\nFanIn returns unbuffered channel of generic type `T` which serves as delivery pipeline for the values received from at least 2 incoming channels, it's closed once all of the incoming channels closed or done is closed\n\n### func FanInWithContext\n\n```go\nfunc FanInWithContext[T any](ctx context.Context, ch1, ch2 \u003c-chan T, channels ...\u003c-chan T) (\u003c-chan T, error)\n```\n\nFanInWithContext returns unbuffered channel of generic type `T` which serves as delivery pipeline for the values received from at least 2 incoming channels, it's closed once all of the incoming channels closed or context cancelled.\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/butuzov/harmony\"\n)\n\nfunc main() {\n\t// return channel that generate\n\tfiller := func(start, stop int) chan int {\n\t\tch := make(chan int)\n\n\t\tgo func() {\n\t\t\tdefer close(ch)\n\t\t\tfor i := start; i \u003c= stop; i++ {\n\t\t\t\tch \u003c- i\n\t\t\t}\n\t\t}()\n\n\t\treturn ch\n\t}\n\n\tch1 := filler(10, 12)\n\tch2 := filler(12, 14)\n\tch3 := filler(15, 16)\n\n\tctx := context.Background()\n\tif ch, err := harmony.FanInWithContext(ctx, ch1, ch2, ch3); err != nil {\n\t\tfor val := range ch {\n\t\t\tfmt.Println(val)\n\t\t}\n\t}\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n### func Futute\n\n```go\nfunc Futute[T any](done \u003c-chan struct{}, futureFn func() T) (\u003c-chan T, error)\n```\n\nFutute.T any. will return buffered channel of size 1 and generic type `T`, which will eventually contain the results of the execution `futureFn``, or be closed in case if context cancelled.\n\n### func FututeWithContext\n\n```go\nfunc FututeWithContext[T any](ctx context.Context, futureFn func() T) (\u003c-chan T, error)\n```\n\nFututeWithContext.T any. will return buffered channel of size 1 and generic type `T`, which will eventually contain the results of the execution `futureFn``, or be closed in case if context cancelled.\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/butuzov/harmony\"\n)\n\nfunc main() {\n\t// Requests random dogs picture from dog.ceo (dog as service)\n\tctx := context.Background()\n\ta, _ := harmony.FututeWithContext(ctx, func() int { return 1 })\n\tb, _ := harmony.FututeWithContext(ctx, func() int { return 0 })\n\tfmt.Println(\u003c-a, \u003c-b)\n}\n```\n\n#### Output\n\n```\n1 0\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eExample (Dogs_as_service)\u003c/summary\u003e\n\u003cp\u003e\n\nFututeWithContext is shows creation of two \"futures\" that are used in our \"rate our dogs\" startup.\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"github.com/butuzov/harmony\"\n\t\"io/ioutil\"\n\t\"log\"\n\t\"net/http\"\n\t\"time\"\n)\n\nfunc main() {\n\t// Requests random dogs picture from dog.ceo (dog as service)\n\tgetRandomDogPicture := func() string {\n\t\tvar data struct {\n\t\t\tMessage string \"json:'message'\"\n\t\t}\n\n\t\tconst API_URL = \"https://dog.ceo/api/breeds/image/random\"\n\t\tctx := context.Background()\n\n\t\tif req, err := http.NewRequestWithContext(ctx, http.MethodGet, API_URL, nil); err != nil {\n\t\t\tlog.Println(fmt.Errorf(\"request: %w\", err))\n\t\t\treturn \"\"\n\t\t} else if res, err := http.DefaultClient.Do(req); err != nil {\n\t\t\tlog.Println(fmt.Errorf(\"request: %w\", err))\n\t\t\treturn \"\"\n\t\t} else {\n\t\t\tdefer res.Body.Close()\n\n\t\t\tif body, err := ioutil.ReadAll(res.Body); err != nil {\n\t\t\t\tlog.Println(fmt.Errorf(\"reading body: %w\", err))\n\t\t\t\treturn \"\"\n\t\t\t} else if err := json.Unmarshal(body, \u0026data); err != nil {\n\t\t\t\tlog.Println(fmt.Errorf(\"unmarshal: %w\", err))\n\t\t\t\treturn \"\"\n\t\t\t}\n\t\t}\n\n\t\treturn data.Message\n\t}\n\n\ta, _ := harmony.FututeWithContext(context.Background(), func() string {\n\t\treturn getRandomDogPicture()\n\t})\n\n\tctx, cancel := context.WithTimeout(context.Background(), 250*time.Millisecond)\n\tdefer cancel()\n\tb, _ := harmony.FututeWithContext(ctx, func() string {\n\t\treturn getRandomDogPicture()\n\t})\n\tfmt.Printf(\"Rate My Dog: ..a) %s..b) %s.\", \u003c-a, \u003c-b)\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n### func OrDone\n\n```go\nfunc OrDone[T any](done \u003c-chan struct{}, incoming \u003c-chan T) (\u003c-chan T, error)\n```\n\nOrDone will return a new unbuffered channel of type `T` that serves as a pipeline for the incoming channel. Channel is closed once the context is canceled or the incoming channel is closed. This is variation or the pattern that usually called `OrWithDone` or`Cancel`.\n\n### func OrDoneWithContext\n\n```go\nfunc OrDoneWithContext[T any](ctx context.Context, incoming \u003c-chan T) (\u003c-chan T, error)\n```\n\nOrDoneWithContext will return a new unbuffered channel of type `T` that serves as a pipeline for the incoming channel. Channel is closed once the context is canceled or the incoming channel is closed. This is variation or the pattern that usually called `OrWithDone` or`Cancel`.\n\n### func Pipeline\n\n```go\nfunc Pipeline[T1, T2 any](done \u003c-chan struct{}, incomingCh \u003c-chan T1, totalWorkers int, workerFn func(T1) T2) (\u003c-chan T2, error)\n```\n\nPipeline returns the channel of generic type `T2` that can serve as a pipeline for the next stage. It's implemented in almost same manner as a `WorkerPool` and allows to specify number of workers that going to proseed values received from the incoming channel. Outgoing channel is going to be closed once the incoming chan is closed or context canceld.\n\n### func PipelineWithContext\n\n```go\nfunc PipelineWithContext[T1, T2 any](ctx context.Context, incomingCh \u003c-chan T1, totalWorkers int, workerFn func(T1) T2) (\u003c-chan T2, error)\n```\n\nPipelineWithContext returns the channel of generic type `T2` that can serve as a pipeline for the next stage. It's implemented in almost same manner as a `WorkerPool` and allows to specify number of workers that going to proseed values received from the incoming channel. Outgoing channel is going to be closed once the incoming chan is closed or context canceld.\n\n\u003cdetails\u003e\u003csummary\u003eExample (0rimes)\u003c/summary\u003e\n\u003cp\u003e\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/butuzov/harmony\"\n\t\"log\"\n\t\"math\"\n\t\"time\"\n)\n\nfunc main() {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)\n\tdefer cancel()\n\n\tvar (\n\t\tincomingCh = make(chan uint64)\n\t\tisPrime    = func(n uint64) bool {\n\t\t\tfor i := uint64(2); i \u003c (n/2)+1; i++ {\n\t\t\t\tif n%i == 0 {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\t)\n\n\tvar results []uint64\n\tworkerFunc := func(n uint64) uint64 {\n\t\tif isPrime(n) {\n\t\t\treturn n\n\t\t}\n\t\treturn 0\n\t}\n\n\t// Producer: Initial numbers\n\tgo func() {\n\t\tfor i := uint64(0); i \u003c math.MaxUint64; i++ {\n\t\t\tincomingCh \u003c- i\n\t\t}\n\t}()\n\n\tif ch, err := harmony.PipelineWithContext(ctx, incomingCh, 100, workerFunc); err != nil {\n\t\tlog.Printf(\"Error: %v\", err)\n\t} else {\n\t\tfor val := range ch {\n\t\t\tif val == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tresults = append(results, val)\n\t\t}\n\t\tfmt.Println(results)\n\t}\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n### func Queue\n\n```go\nfunc Queue[T any](done \u003c-chan struct{}, genFn func() T) (\u003c-chan T, error)\n```\n\nQueue returns an unbuffered channel that is populated by func `genFn`. Chan is closed once context is Done. It's similar to `Future` pattern, but doesn't have a limit to just one result.\n\n### func QueueWithContext\n\n```go\nfunc QueueWithContext[T any](ctx context.Context, genFn func() T) (\u003c-chan T, error)\n```\n\nQueueWithContext returns an unbuffered channel that is populated by func `genFn`. Chan is closed once context is Done. It's similar to `Future` pattern, but doesn't have a limit to just one result.\n\n\u003cdetails\u003e\u003csummary\u003eExample\u003c/summary\u003e\n\u003cp\u003e\n\nGenerate fibonacci sequence\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/butuzov/harmony\"\n\t\"log\"\n)\n\nfunc main() {\n\t// fin returns function  that returns Fibonacci sequence up to n element,\n\t// it returns 0 after limit reached.\n\tfib := func(limit int) func() int {\n\t\ta, b, nTh := 0, 1, 1\n\t\treturn func() int {\n\t\t\tif nTh \u003e limit {\n\t\t\t\treturn 0\n\t\t\t}\n\n\t\t\tnTh++\n\t\t\ta, b = b, a+b\n\t\t\treturn a\n\t\t}\n\t}\n\n\tfirst10FibNumbers := make([]int, 10)\n\tincoming, err := harmony.QueueWithContext(context.Background(), fib(10))\n\tif err != nil {\n\t\tlog.Printf(\"err: %v\", err)\n\t\treturn\n\t}\n\n\tfor i := 0; i \u003c cap(first10FibNumbers); i++ {\n\t\tfirst10FibNumbers[i] = \u003c-incoming\n\t}\n\n\tfmt.Println(first10FibNumbers)\n}\n```\n\n#### Output\n\n```\n[1 1 2 3 5 8 13 21 34 55]\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n### func Tee\n\n```go\nfunc Tee[T any](done \u003c-chan struct{}, incoming \u003c-chan T) (\u003c-chan T, \u003c-chan T, error)\n```\n\nTee will return two channels of generic type `T` used to fan\n-out data from the incoming channel. Channels needs to be read in order next iteration over incoming chanel happen.\n\n### func TeeWithContext\n\n```go\nfunc TeeWithContext[T any](ctx context.Context, incoming \u003c-chan T) (\u003c-chan T, \u003c-chan T, error)\n```\n\nTeeWithContext will return two channels of generic type `T` used to fan\n-out data from the incoming channel. Channels needs to be read in order next iteration over incoming chanel happen.\n\n### func WorkerPool\n\n```go\nfunc WorkerPool[T any](done \u003c-chan struct{}, jobQueue chan T, maxWorkers int, workFunc func(T)) error\n```\n\nWorkerPool accepts channel of generic type `T` which is used to serve jobs to max workersTotal workers. Goroutines stop: Once channel closed and drained, or done is closed\n\n### func WorkerPoolWithContext\n\n```go\nfunc WorkerPoolWithContext[T any](ctx context.Context, jobQueue chan T, maxWorkers int, workFunc func(T)) error\n```\n\nWorkerPoolWithContext accepts channel of generic type `T` which is used to serve jobs to max workersTotal workers. Goroutines stop: Once channel closed and drained, or context cancelled.\n\n\u003cdetails\u003e\u003csummary\u003eExample (0rimes)\u003c/summary\u003e\n\u003cp\u003e\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/butuzov/harmony\"\n\t\"math\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n)\n\nfunc main() {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Millisecond)\n\tdefer cancel()\n\n\tvar (\n\t\tprimesCh   = make(chan uint64)\n\t\tincomingCh = make(chan uint64)\n\t\tisPrime    = func(n uint64) bool {\n\t\t\tfor i := uint64(2); i \u003c (n/2)+1; i++ {\n\t\t\t\tif n%i == 0 {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\t\ttotalWorkers = runtime.NumCPU() - 1\n\t)\n\n\t// Producer: Initial numbers\n\tgo func() {\n\t\tfor i := uint64(0); i \u003c math.MaxUint64; i++ {\n\t\t\tincomingCh \u003c- i\n\t\t}\n\t}()\n\n\t// Consumers Worker Pool: checking primes of incoming numbers.\n\tharmony.WorkerPoolWithContext(ctx, incomingCh, totalWorkers, func(n uint64) {\n\t\tif !isPrime(n) {\n\t\t\treturn\n\t\t}\n\t\tprimesCh \u003c- n\n\t})\n\n\tvar results []uint64\n\tvar mu sync.RWMutex\n\tgo func() {\n\t\tfor n := range primesCh {\n\t\t\tmu.Lock()\n\t\t\tresults = append(results, n)\n\t\t\tmu.Unlock()\n\t\t}\n\t}()\n\n\t\u003c-ctx.Done()\n\n\tmu.RLock()\n\tfmt.Println(results)\n\tmu.RUnlock()\n}\n```\n\n\u003c/p\u003e\n\u003c/details\u003e\n\n\u003c!-- End ---\u003e\n\u003c!-- You can Edit Content under this comment ---\u003e\n\n## Resources\n\n* `talk` [Bryan C. Mills - Rethinking Classical Concurrency Patterns](https://www.youtube.com/watch?v=5zXAHh5tJqQ) + [`slides`](https://drive.google.com/file/d/1nPdvhB0PutEJzdCq5ms6UI58dp50fcAN/view) + [`comments+notes`](https://github.com/sourcegraph/gophercon-2018-liveblog/issues/35)\n* `book` [Katherine Cox-Buday - Concurrency In Go](https://www.oreilly.com/library/view/concurrency-in-go/9781491941294/)\n* `talk` [Rob Pike - Concurrency is not Parallelism](https://www.youtube.com/watch?v=oV9rvDllKEg) + [`slides`](https://go.dev/talks/2012/waza.slide)\n* `blog` [Go Concurrency Patterns: Context](https://go.dev/blog/context)\n* `blog` [Go Concurrency Patterns: Pipelines and cancellation](https://go.dev/blog/pipelines)\n* `talk` [Sameer Ajmani  - Advanced Go Concurrency Patterns](https://www.youtube.com/watch?v=QDDwwePbDtw) + [`slides`](https://talks.golang.org/2013/advconc.slide)\n* `talk` [Rob Pike - Go Concurrency Patterns](https://www.youtube.com/watch?v=f6kdp27TYZs) + [`slides`](https://talks.golang.org/2012/concurrency.slide)\n* `blog` [Go Concurrency Patterns: Timing out, moving on](https://go.dev/blog/concurrency-timeouts)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbutuzov%2Fharmony","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbutuzov%2Fharmony","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbutuzov%2Fharmony/lists"}