{"id":13461375,"url":"https://github.com/luk4z7/go-concurrency-guide","last_synced_at":"2025-05-15T10:00:26.849Z","repository":{"id":37772037,"uuid":"430430380","full_name":"luk4z7/go-concurrency-guide","owner":"luk4z7","description":"Practical concurrency guide in Go, communication by channels, patterns","archived":false,"fork":false,"pushed_at":"2023-04-25T00:28:44.000Z","size":78,"stargazers_count":2645,"open_issues_count":1,"forks_count":183,"subscribers_count":25,"default_branch":"main","last_synced_at":"2025-04-14T15:56:54.501Z","etag":null,"topics":["channels","concurrency","deadlock","go","goroutine","guideline","mutex","patterns","race-conditions","synchronization"],"latest_commit_sha":null,"homepage":"","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/luk4z7.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2021-11-21T17:12:51.000Z","updated_at":"2025-04-13T09:24:52.000Z","dependencies_parsed_at":"2024-01-06T14:00:13.797Z","dependency_job_id":"bf577b21-a5b9-4aa2-9aaa-346f58000988","html_url":"https://github.com/luk4z7/go-concurrency-guide","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/luk4z7%2Fgo-concurrency-guide","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luk4z7%2Fgo-concurrency-guide/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luk4z7%2Fgo-concurrency-guide/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/luk4z7%2Fgo-concurrency-guide/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/luk4z7","download_url":"https://codeload.github.com/luk4z7/go-concurrency-guide/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254319715,"owners_count":22051072,"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":["channels","concurrency","deadlock","go","goroutine","guideline","mutex","patterns","race-conditions","synchronization"],"created_at":"2024-07-31T11:00:36.563Z","updated_at":"2025-05-15T10:00:26.170Z","avatar_url":"https://github.com/luk4z7.png","language":"Go","funding_links":[],"categories":["Go","指南","Guide","Uncategorized","Repositories"],"sub_categories":["组织","To Organize","Uncategorized"],"readme":"\n# Go Concurrency Guide\n\nThis guide is built on top of the some examples of the book `Go Concurrency in Go` and `Go Programming Language`\n\n- [Race Condition and Data Race](#race-condition-and-data-race)\n- [Memory Access Synchronization](#memory-access-synchronization)\n    -  [Mutex](#mutex)\n    -  [WaitGroup](#waitgroup)\n    -  [RWMutex](#rwmutex)\n    -  [Cond](#cond)\n    -  [Pool](#pool)\n- [Deadlocks, Livelocks and Starvation](#deadlocks-livelocks-and-starvation)\n    -  [Deadlocks](#deadlocks)\n    -  [Livelocks](#livelocks)\n    -  [Starvation](#starvation)\n- [Channels](#channels)\n- [Patterns](#patterns)\n    - [Confinement](#confinement)\n    - [Cancellation](#cancellation)\n    - [OR Channel](#or-channel)\n    - [Error Handling](#error-handling)\n    - [Pipelines](#pipelines)\n    - [Fan-in and Fan-out](#fan-in-and-fan-out)\n    - [Or done channel](#or-done-channel)\n    - [Tee channel](#tee-channel)\n    - [Bridge channel](#bridge-channel)\n    - [Queuing](#queuing)\n    - [Context package](#context-package)\n    - [HeartBeats](#heartbeats)\n    - [Replicated Requests](#replicated-requests)\n- [Scheduler Runtime](#scheduler-runtime)\n- [References](#references)\n\n\n## Race Condition and Data Race\n\nRace condition occur when two or more operations must execute in the correct order, but the program has not been written so that this order is guaranteed  to be maintained.\n\nData race is when one concurrent operation attempts to read a variable while at some undetermined time another concurrent operation is attempting to write to the same variable. The main func is the main goroutine.\n\n```go\nfunc main() {\n    var data int\n    go func() {\n        data++\n    }()\n\n    if data == 0 {\n        fmt.Printf(\"the value is %d\", data)\n    }\n}\n```\n\n\n## Memory Access Synchronization\n\nThe sync package contains the concurrency primitives that are most useful for low-level memory access synchronization.\nCritical section is the place in your code that has access to a shared memory\n\n\n### Mutex\n\nMutex stands for “mutual exclusion” and is a way to protect critical sections of your program.\n\n```go\ntype Counter struct {\n    mu sync.Mutex\n    value int\n}\n\nfunc (c *Counter) Increment() {\n    c.mu.Lock()\n    defer c.mu.Unlock()\n    c.value++\n}\n```\n\n\n### WaitGroup\n\nCall to add a group of goroutines\n\n```go\nvar wg sync.WaitGroup\nfor _, salutation := range []string{\"hello\", \"greetings\", \"good day\"} {\n    wg.Add(1)\n    go func(salutation string) { \n        defer wg.Done()\n        fmt.Println(salutation)\n    }(salutation) \n}\nwg.Wait()\n```\n\n\n### RWMutex\n\nMore fine-grained memory control, being possible to request read-only lock\n\n```go\nproducer := func(wg *sync.WaitGroup, l sync.Locker) { \n    defer wg.Done()\n    for i := 5; i \u003e 0; i-- {\n        l.Lock()\n        l.Unlock()\n        time.Sleep(1) \n    }\n}\n\nobserver := func(wg *sync.WaitGroup, l sync.Locker) {\n    defer wg.Done()\n    l.Lock()\n    defer l.Unlock()\n}\n\ntest := func(count int, mutex, rwMutex sync.Locker) time.Duration {\n    var wg sync.WaitGroup\n    wg.Add(count+1)\n    beginTestTime := time.Now()\n    go producer(\u0026wg, mutex)\n    for i := count; i \u003e 0; i-- {\n        go observer(\u0026wg, rwMutex)\n    }\n\n    wg.Wait()\n    return time.Since(beginTestTime)\n}\n\ntw := tabwriter.NewWriter(os.Stdout, 0, 1, 2, ' ', 0)\ndefer tw.Flush()\n\nvar m sync.RWMutex\nfmt.Fprintf(tw, \"Readers\\tRWMutex\\tMutex\\n\")\n\nfor i := 0; i \u003c 20; i++ {\n    count := int(math.Pow(2, float64(i)))\n    fmt.Fprintf(\n        tw,\n        \"%d\\t%v\\t%v\\n\",\n        count,\n        test(count, \u0026m, m.RLocker()),\n        test(count, \u0026m, \u0026m),\n    )\n}\n```\n\n\n### Cond\n\nIt would be better if there were some kind of way for a goroutine to efficiently sleep until it was signaled to wake and check its condition. This is exactly what the Cond type does for us.\n\nThe Cond and the Broadcast is the method that provides for notifying goroutines blocked on Wait call that the condition has been triggered.\n\n```go\ntype Button struct {\n    Clicked *sync.Cond\n}\n\nfunc main() {\n    button := Button{\n        Clicked: sync.NewCond(\u0026sync.Mutex{}),\n    }\n\n    // running on goroutine every function that passed/registered\n    // and wait, not exit until that goroutine is confirmed to be running\n    subscribe := func(c *sync.Cond, param string, fn func(s string)) {\n        var goroutineRunning sync.WaitGroup\n        goroutineRunning.Add(1)\n\n        go func(p string) {\n            goroutineRunning.Done()\n            c.L.Lock() // critical section\n            defer c.L.Unlock()\n\n            fmt.Println(\"Registered and wait ... \")\n            c.Wait()\n\n            fn(p)\n        }(param)\n\n        goroutineRunning.Wait()\n    }\n\n    var clickRegistered sync.WaitGroup\n\n    for _, v := range []string{\n        \"Maximizing window.\",\n        \"Displaying annoying dialog box!\",\n        \"Mouse clicked.\"} {\n\n        clickRegistered.Add(1)\n\n        subscribe(button.Clicked, v, func(s string) {\n            fmt.Println(s)\n            clickRegistered.Done()\n        })\n    }\n\n    button.Clicked.Broadcast()\n\n    clickRegistered.Wait()\n}\n```\n[cond samples](https://github.com/luk4z7/go-concurrency-guide/blob/main/sync/cond/main.go)\n\n\n### Once\n\nEnsuring that only one execution will be carried out even among several goroutines\n\n```go\nvar count int\n\nincrement := func() {\n    count++\n}\n\nvar once sync.Once\n\nvar increments sync.WaitGroup\nincrements.Add(100)\n\nfor i := 0; i \u003c 100; i++ {\n    go func() {\n        defer increments.Done()\n        once.Do(increment)\n    }()\n}\n\nincrements.Wait()\nfmt.Printf(\"Count is %d\\n\", count)\n```\n\n\n### Pool\n\nManager the pool of connections, a quantity\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"sync\"\n)\n\nfunc main() {\n    myPool := \u0026sync.Pool{\n        New: func() interface{} {\n            fmt.Println(\"Creating new instance.\")\n\n            return struct{}{}\n        },\n    }\n\n    // Get call New function defined in pool if there is no instance started\n    myPool.Get()\n    instance := myPool.Get()\n    fmt.Println(\"instance\", instance)\n\n    // here we put a previously retrieved instance back into the pool, \n    // this increases the number of instances available to one\n    myPool.Put(instance)\n\n    // when this call is executed, we will reuse the \n    // previously allocated instance and put it back in the pool\n    myPool.Get()\n\n    var numCalcsCreated int\n    calcPool := \u0026sync.Pool{\n        New: func() interface{} {\n            fmt.Println(\"new calc pool\")\n\n            numCalcsCreated += 1\n            mem := make([]byte, 1024)\n\n            return \u0026mem\n        },\n    }\n\n    fmt.Println(\"calcPool.New\", calcPool.New())\n\n    calcPool.Put(calcPool.New())\n    calcPool.Put(calcPool.New())\n    calcPool.Put(calcPool.New())\n    calcPool.Put(calcPool.New())\n\n    calcPool.Get()\n\n    const numWorkers = 1024 * 1024\n    var wg sync.WaitGroup\n    wg.Add(numWorkers)\n\n    for i := numWorkers; i \u003e 0; i-- {\n        go func() {\n            defer wg.Done()\n\n            mem := calcPool.Get().(*[]byte)\n            defer calcPool.Put(mem)\n\n            // Assume something interesting, but quick is being done with\n            // this memory.\n        }()\n    }\n\n    wg.Wait()\n    fmt.Printf(\"%d calculators were created.\", numCalcsCreated)\n}\n```\n[sync samples](https://github.com/luk4z7/go-concurrency-guide/tree/main/sync)\n\n\n\n## Deadlocks, Livelocks, and Starvation\n\n### Deadlocks\n\nDeadlocks is a program is one in which all concurrent processes are waiting on one another.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"sync\"\n    \"time\"\n)\n\ntype value struct {\n    mu    sync.Mutex\n    value int\n}\n\nfunc main() {\n    var wg sync.WaitGroup\n    printSum := func(v1, v2 *value) {\n        defer wg.Done()\n        v1.mu.Lock()\n        defer v1.mu.Unlock()\n\n        // deadlock\n        time.Sleep(2 * time.Second)\n        v2.mu.Lock()\n        defer v2.mu.Unlock()\n\n        fmt.Printf(\"sum=%v\\n\", v1.value+v2.value)\n    }\n\n    var a, b value\n    wg.Add(2)\n    go printSum(\u0026a, \u0026b)\n    go printSum(\u0026b, \u0026a)\n\n    wg.Wait()\n}\n```\n\n```go\npackage main\n\nfunc main() {\n    message := make(chan string)\n\n    // A goroutine ( main goroutine ) trying to send message to channel\n    message \u003c- \"message\" // fatal error: all goroutines are asleep - deadlock!\n}\n```\n\n```go\npackage main\n\nfunc main() {\n    message := make(chan string)\n\n    // A goroutine ( main goroutine ) trying to receive message from channel\n    \u003c-message // fatal error: all goroutines are asleep - deadlock!\n}\n```\n\n\n### Livelocks\n\nLivelocks are programs that are actively performing concurrent operations, but these operations do nothing to move the state of the program forward.\n\n```go\npackage main\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\nfunc main() {\n\tcadence := sync.NewCond(\u0026sync.Mutex{})\n\tgo func() {\n\t\tfor range time.Tick(1 * time.Millisecond) {\n\t\t\tcadence.Broadcast()\n\t\t}\n\t}()\n\n\ttakeStep := func() {\n\t\tcadence.L.Lock()\n\t\tcadence.Wait()\n\t\tcadence.L.Unlock()\n\t}\n\n\ttryDir := func(dirName string, dir *int32, out *bytes.Buffer) bool {\n\t\tfmt.Fprintf(out, \" %v\", dirName)\n\t\tatomic.AddInt32(dir, 1)\n\t\ttakeStep()\n\n\t\tif atomic.LoadInt32(dir) == 1 {\n\t\t\tfmt.Fprint(out, \" . Success!\")\n\n\t\t\treturn true\n\t\t}\n\n\t\ttakeStep()\n\t\tatomic.AddInt32(dir, -1)\n\n\t\treturn false\n\t}\n\n\tvar left, right int32\n\ttryLeft := func(out *bytes.Buffer) bool {\n\t\treturn tryDir(\"left\", \u0026left, out)\n\t}\n\n\ttryRight := func(out *bytes.Buffer) bool {\n\t\treturn tryDir(\"right\", \u0026right, out)\n\t}\n\n\twalk := func(walking *sync.WaitGroup, name string) {\n\t\tvar out bytes.Buffer\n\t\tdefer func() {\n\t\t\tfmt.Println(out.String())\n\t\t}()\n\t\tdefer walking.Done()\n\n\t\tfmt.Fprintf(\u0026out, \"%v is trying to scoot:\", name)\n\t\tfor i := 0; i \u003c 5; i++ {\n\t\t\tif tryLeft(\u0026out) || tryRight(\u0026out) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tfmt.Fprintf(\u0026out, \"\\n%v tosses her hands up in exasperation\", name)\n\t}\n\n\tvar peopleInHallway sync.WaitGroup\n\tpeopleInHallway.Add(2)\n\n\tgo walk(\u0026peopleInHallway, \"Alice\")\n\tgo walk(\u0026peopleInHallway, \"Barbara\")\n\tpeopleInHallway.Wait()\n}\n```\n\n\n### Starvation\n\nStarvation is any situation where a concurrent process cannot get all the resources it needs to perform work.\n\n```go\npackage main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n)\n\nfunc main() {\n\tfmt.Println(\"vim-go\")\n\n\tvar wg sync.WaitGroup\n\tvar sharedLock sync.Mutex\n\tconst runtime = 1 * time.Second\n\n\tgreedyWorker := func() {\n\t\tdefer wg.Done()\n\n\t\tvar count int\n\t\tfor begin := time.Now(); time.Since(begin) \u003c= runtime; {\n\t\t\tsharedLock.Lock()\n\t\t\ttime.Sleep(3 * time.Nanosecond)\n\t\t\tsharedLock.Unlock()\n\t\t\tcount++\n\t\t}\n\n\t\tfmt.Printf(\"Greedy worker was able to execute %v work loops\\n\", count)\n\t}\n\n\tpoliteWorker := func() {\n\t\tdefer wg.Done()\n\n\t\tvar count int\n\t\tfor begin := time.Now(); time.Since(begin) \u003c= runtime; {\n\t\t\tsharedLock.Lock()\n\t\t\ttime.Sleep(1 * time.Nanosecond)\n\t\t\tsharedLock.Unlock()\n\n\t\t\tsharedLock.Lock()\n\t\t\ttime.Sleep(1 * time.Nanosecond)\n\t\t\tsharedLock.Unlock()\n\n\t\t\tsharedLock.Lock()\n\t\t\ttime.Sleep(1 * time.Nanosecond)\n\t\t\tsharedLock.Unlock()\n\n\t\t\tcount++\n\t\t}\n\n\t\tfmt.Printf(\"Polite worker was able to execute %v work loops \\n\", count)\n\t}\n\n\twg.Add(2)\n\tgo greedyWorker()\n\tgo politeWorker()\n\twg.Wait()\n}\n```\n\n\n## Channels\n\nChannels are one of the synchronization primitives in Go derived from Hoare’s CSP. While they can be used to synchronize access of the memory, they are best used to communicate information between goroutines, default value for channel: `nil`.\n\nTo declare a channel to read and send\n```go\nstream := make(chan interface{})\n```\n\nTo declare unidirectional channel that only can read\n```go\nstream := make(\u003c-chan interface{})\n```\n\nTo declare unidirectional channel that only can send\n```go\nstream := make(chan\u003c- interface{})\n```\n\nis not often see the instantiates channels unidirectional, only in parameters in functions, is common because Go convert them implicity\n```go\nvar receiveChan \u003c-chan interface{}\nvar sendChan chan\u003c- interface{}\ndataStream := make(chan interface{})\n\n// Valid statements:\nreceiveChan = dataStream\nsendChan = dataStream\n```\n\nTo receive\n```go\n\u003c-stream\n```\n\nto send\n```go\nstream \u003c- \"Hello world\"\n```\n\nRanging over a channel\nthe for range break the loop if the channel is closed\n\n```go\nintStream := make(chan int)\ngo func() {\n    defer close(intStream) \n    for i := 1; i \u003c= 5; i++ {\n        intStream \u003c- i\n    }\n}()\n\nfor integer := range intStream {\n    fmt.Printf(\"%v \", integer)\n}\n```\n\n**unbuffered channel**\u003cbr/\u003e\nA send operation on an unbuffered channel blocks the sending goroutine, until another goroutine performs a corresponding receive on the same channel; at that point, the value is passed, and both goroutines can continue. On the other hand, if a receive operation is attempted beforehand, the receiving goroutine is blocked until another goroutine performs a send on the same channel. Communication over an unbuffered channel makes the sending and receiving goroutines synchronize. Because of this, unbuffered channels are sometimes called synchronous channels. When a value is sent over an unbuffered channel, the reception of the value takes place before the sending goroutine wakes up again. In discussions of concurrency, when we say that x occurs before y, we do not simply mean that x occurs before y in time; we mean that this is guaranteed and that all your previous effects like updates to variables will complete and you can count on them. When x does not occur before y or after y, we say that x is concurrent with y. This is not to say that x and y are necessarily simultaneous; it just means that we can't assume anything about your order\n\n**buffered channel**\u003cbr/\u003e\nboth, read and write of a channel full or empty it will block, on the buffered channel \n\n```go\nvar dataStream chan interface{}\ndataStream = make(chan interface{}, 4)\n```\n\nboth, read and send a channel empty cause deadlock\n\n```go\nvar dataStream chan interface{}\n\u003c-dataStream // This panics with: fatal error: all goroutines are asleep - deadlock!\n```\n```\n   goroutine 1 [chan receive (nil chan)]:\n   main.main()\n       /tmp/babel-23079IVB/go-src-23079O4q.go:9 +0x3f\n   exit status 2\n```\n\n```go\nvar dataStream chan interface{}\ndataStream \u003c- struct{}{} // This produces: fatal error: all goroutines are asleep - deadlock!\n```\n```\n  goroutine 1 [chan send (nil chan)]:\n  main.main()\n      /tmp/babel-23079IVB/go-src-23079dnD.go:9 +0x77\n  exit status 2\n```\n\nand a close channel cause a panic\n\n```go\nvar dataStream chan interface{}\nclose(dataStream) // This produces: panic: close of nil channel\n```\n```\n  goroutine 1 [running]:\n  panic(0x45b0c0, 0xc42000a160)\n      /usr/local/lib/go/src/runtime/panic.go:500 +0x1a1\n  main.main()\n      /tmp/babel-23079IVB/go-src-230794uu.go:9 +0x2a\n  exit status 2 Yipes! This is probably\n```\n\nTable with result of channel operations\n\nOperation | Channel State      | Result\n----------|--------------------|-------------\nRead      | nil                | Block\n_         | Open and Not Empty | Value\n_         | Open and Empty     | Block\n_         | Close              | default value, false\n_         | Write Only         | Compilation Error\nWrite     | nil                | Block\n_         | Open and Full      | Block\n_         | Open and Not Full  | Write Value\n_         | Closed             | panic\n_         | Receive Only       | Compilation Error\nClose     | nil                | panic \n_         | Open and Not Empty | Closes Channel; reads succeed until channel is drained, then reads produce default value\n_         | Open and Empty     | Closes Channel; reads produces default value\n_         | Closed             | panic\n\n\n`TIP: Cannot close a receive-only channel`\n\n* Let's start with channel owners. The goroutine that has a channel must:\n    * 1 - Instantiate the channel.  \n    * 2 - Perform writes, or pass ownership to another goroutine.  \n    * 3 - Close the channel.  \n    * 4 - Encapsulate the previous three things in this list and expose them via a reader channel.\n\n* When assigning channel owners responsibilities, a few things happen:\n    * 1 - Because we’re the one initializing the channel, we remove the risk of deadlocking by writing to a nil channel.  \n    * 2 - Because we’re the one initializing the channel, we remove the risk of panicing by closing a nil channel.  \n    * 3 - Because we’re the one who decides when the channel gets closed, we remove the risk of panicing by writing to a closed channel.  \n    * 4 - Because we’re the one who decides when the channel gets closed, we remove the risk of panicing by closing a channel more than once.  \n    * 5 - We wield the type checker at compile time to prevent improper writes to our channel.\n\n```go\nchanOwner := func() \u003c-chan int {\n    resultStream := make(chan int, 5) \n    go func() { \n        defer close(resultStream) \n        for i := 0; i \u003c= 5; i++ {\n            resultStream \u003c- i\n        }\n    }()\n    return resultStream \n}\n\nresultStream := chanOwner()\nfor result := range resultStream { \n    fmt.Printf(\"Received: %d\\n\", result)\n}\n\nfmt.Println(\"Done receiving!\")\n```\n\nThe creation of channel owners explicitly tends to have greater control of when that channel should be closed and its operation, avoiding the delegation of these functions to other methods/functions of the system, avoiding reading closed channels or sending data to the same already finalized\n\n\n**select**\n\nthe select cases do not work the same as the switch, which is sequential, and the execution will not automatically fall if none of the criteria is met.\n\n```go\nvar c1, c2 \u003c-chan interface{}\nvar c3 chan\u003c- interface{}\nselect {\ncase \u003c- c1:\n    // Do something\ncase \u003c- c2:\n    // Do something\ncase c3\u003c- struct{}{}:\n\n}\n```\n\nInstead, all channel reads and writes are considered simultaneously to see if any of them are ready: channels filled or closed in the case of reads and channels not at capacity in the case of writes. If none of the channels are ready, the entire select command is blocked. Then, when one of the channels is ready, the operation will proceed and its corresponding instructions will be executed.\n\n```go\nstart := time.Now()\nc := make(chan interface{})\ngo func() {\n    time.Sleep(5*time.Second)\n    close(c) \n}()\n\nfmt.Println(\"Blocking on read...\")\nselect {\ncase \u003c-c: \n    fmt.Printf(\"Unblocked %v later.\\n\", time.Since(start))\n}\n```\n\nquestions when work with select and channels\n\n1 - What happens when multiple channels have something to read?  \n\n```go\nc1 := make(chan interface{}); close(c1)\nc2 := make(chan interface{}); close(c2)\n\nvar c1Count, c2Count int\nfor i := 1000; i \u003e= 0; i-- {\n    select {\n    case \u003c-c1:\n        c1Count++\n    case \u003c-c2:\n        c2Count++\n    }\n}\n\nfmt.Printf(\"c1Count: %d\\nc2Count: %d\\n\", c1Count, c2Count)\n```\n\nThis produces:\u003cbr/\u003e\nc1Count: 505\u003cbr/\u003e\nc2Count: 496\u003cbr/\u003e\n\nhalf is read by c1 half by c2 by the Go runtime, cannot exactly predict how much each will be read, and will not be exactly the same for both, it can happen but cannot be predicted, the runtime knows nothing about the intent to own 2 channels receiving information or closed as in our example, then the runtime includes a pseudo-random\nGo runtime will perform a pseudo-random uniform selection over the select case statement set. This just means that from your set of cases, each one has the same chance of being selected as all the others. \n\nA good way to do this is to introduce a random variable into your equation - in this case, which channel to select from. By weighing the chance that each channel is used equally, all Go programs that use the select statement will perform well in the average case.\n\n\n2 - What if there are never any channels that become ready?  \n\n```go\nvar c \u003c-chan int\nselect {\ncase \u003c-c: \ncase \u003c-time.After(1 * time.Second):\n    fmt.Println(\"Timed out.\")\n}\n\n```\n\nTo solve the problem of the channels being blocked, the default can be used to perform some other operation, or in the first example\na time out with time.After\n\n3 - What if we want to do something but no channels are currently ready? use `default`\n\n```go\nstart := time.Now()\nvar c1, c2 \u003c-chan int\nselect {\ncase \u003c-c1:\ncase \u003c-c2:\ndefault:\n    fmt.Printf(\"In default after %v\\n\\n\", time.Since(start))\n}\n```\n\nexit a select block \n\n```go\ndone := make(chan interface{})\ngo func() {\n    time.Sleep(5*time.Second)\n    close(done)\n}()\n\nworkCounter := 0\nloop:\nfor {\n    select {\n    case \u003c-done:\n        break loop\n    default:\n    }\n\n    // Simulate work\n    workCounter++\n    time.Sleep(1*time.Second)\n}\n\nfmt.Printf(\"Achieved %v cycles of work before signalled to stop.\\n\", workCounter)\n```\n\nblock forever \n\n```go\nselect {}\n```\n\n**GOMAXPROCS**\u003cbr/\u003e\nPrior to Go 1.5, GOMAXPROCS was always set to one, and usually you’d find this snippet in most Go programs:\n\n```go\nruntime.GOMAXPROCS(runtime.NumCPU())\n```\n\nThis function controls the number of operating system threads that will host so-called “Work Queues.”\n[documentation](https://pkg.go.dev/runtime#GOMAXPROCS)\n\n\n[Use a sync.Mutex or a channel?](https://github.com/golang/go/wiki/MutexOrChannel)\n\nAs a general guide, though:\n\nChannel | Mutex\n--------|-------\npassing ownership of data, \u003cbr/\u003edistributing units of work, \u003cbr/\u003ecommunicating async results | caches, \u003cbr/\u003estate\n\n\n_\"Do not communicate by sharing memory; instead, share memory by communicating. (copies)\"_\n\n\n\n## Patterns\n\n### Confinement\n\nConfinement is the simple yet powerful idea of ensuring information is only ever available from one concurrent process.\nThere are two kinds of confinement possible: ad hoc and lexical.\n\nAd hoc confinement is when you achieve confinement through a convention\n\n```go\ndata := make([]int, 4)\n\nloopData := func(handleData chan\u003c- int) {\n    defer close(handleData)\n    for i := range data {\n        handleData \u003c- data[i]\n    }\n}\n\nhandleData := make(chan int)\ngo loopData(handleData)\n\nfor num := range handleData {\n    fmt.Println(num)\n}\n```\n\nLexical confinement involves using lexical scope to expose only the correct data and concurrency primitives for multiple concurrent processes to use.\n\n```go\nchanOwner := func() \u003c-chan int {\n    results := make(chan int, 5) \n    go func() {\n        defer close(results)\n\n        for i := 0; i \u003c= 5; i++ {\n            results \u003c- i\n        }\n    }()\n    return results\n}\n\nconsumer := func(results \u003c-chan int) { \n    for result := range results {\n        fmt.Printf(\"Received: %d\\n\", result)\n    }\n    fmt.Println(\"Done receiving!\")\n}\n\nresults := chanOwner()\nconsumer(results)\n```\n[sample](https://github.com/luk4z7/go-concurrency-guide/tree/main/patterns/confinement)\n\n\n### Cancellation\n\n```go\npackage main\n\nfunc main() {\n    doWork := func(\n        done \u003c-chan interface{},\n        strings \u003c-chan string,\n    ) \u003c-chan interface{} {\n        terminated := make(chan interface{})\n        go func() {\n            defer fmt.Println(\"doWork exited.\")\n            defer close(terminated)\n            for {\n\n                select {\n                case s := \u003c-strings:\n                    // Do something interesting\n                    fmt.Println(s)\n                case \u003c-done:\n                    return\n                }\n            }\n        }()\n        return terminated\n    }\n\n    done := make(chan interface{})\n    terminated := doWork(done, nil)\n\n    go func() {\n        // Cancel the operation after 1 second.\n        time.Sleep(1 * time.Second)\n        fmt.Println(\"Canceling doWork goroutine...\")\n        close(done)\n    }()\n\n    \u003c-terminated\n    fmt.Println(\"Done.\")\n}\n```\n[sample](https://github.com/luk4z7/go-concurrency-guide/tree/main/patterns/cancellation)\n\n\n### OR Channel\n\nAt times you may find yourself wanting to combine one or more done channels into a single done channel that closes if any of its component channels close.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"time\"\n)\n\nfunc main() {\n    var or func(channels ...\u003c-chan interface{}) \u003c-chan interface{}\n\n    or = func(channels ...\u003c-chan interface{}) \u003c-chan interface{} {\n        switch len(channels) {\n        case 0:\n            return nil\n        case 1:\n            return channels[0]\n        }\n\n        orDone := make(chan interface{})\n        go func() {\n            defer close(orDone)\n\n            switch len(channels) {\n            case 2:\n                select {\n                case \u003c-channels[0]:\n                case \u003c-channels[1]:\n                }\n            default:\n                select {\n                case \u003c-channels[0]:\n                case \u003c-channels[1]:\n                case \u003c-channels[2]:\n\n                case \u003c-or(append(channels[3:], orDone)...):\n                }\n            }\n        }()\n\n        return orDone\n    }\n\n    sig := func(after time.Duration) \u003c-chan interface{} {\n        c := make(chan interface{})\n        go func() {\n            defer close(c)\n            time.Sleep(after)\n        }()\n        return c\n    }\n\n    start := time.Now()\n    \u003c-or(\n        sig(2*time.Hour),\n        sig(5*time.Minute),\n        sig(1*time.Second),\n        sig(1*time.Hour),\n        sig(1*time.Minute),\n    )\n\n    fmt.Printf(\"done after %v\", time.Since(start))\n}\n```\n[sample](https://github.com/luk4z7/go-concurrency-guide/tree/main/patterns/orchannel)\n\n\n### Error Handling\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"net/http\"\n)\n\ntype Result struct {\n    Error    error\n    Response *http.Response\n}\n\nfunc main() {\n    checkStatus := func(done \u003c-chan interface{}, urls ...string) \u003c-chan Result {\n        results := make(chan Result)\n        go func() {\n            defer close(results)\n\n            for _, url := range urls {\n                var result Result\n                resp, err := http.Get(url)\n                result = Result{Error: err, Response: resp}\n\n                select {\n                case \u003c-done:\n                    return\n                case results \u003c- result:\n                }\n            }\n        }()\n\n        return results\n    }\n\n    done := make(chan interface{})\n    defer close(done)\n\n    urls := []string{\"https://www.google.com\", \"https://badhost\"}\n    for result := range checkStatus(done, urls...) {\n        if result.Error != nil {\n            fmt.Printf(\"error: %v\", result.Error)\n            continue\n        }\n\n        fmt.Printf(\"Response: %v\\n\", result.Response.Status)\n    }\n}\n```\n[sample](https://github.com/luk4z7/go-concurrency-guide/tree/main/patterns/errorhandler)\n\n\n### Pipelines\n\nA pipeline is just another tool you can use to form an abstraction in your system.\n\n```go\nmultiply := func(values []int, multiplier int) []int {\n    multipliedValues := make([]int, len(values))\n    for i, v := range values {\n        multipliedValues[i] = v * multiplier\n    }\n\n    return multipliedValues\n}\n\nadd := func(values []int, additive int) []int {\n    addedValues := make([]int, len(values))\n    for i, v := range values {\n        addedValues[i] = v + additive\n    }\n\n    return addedValues\n}\n\nints := []int{1, 2, 3, 4}\nfor _, v := range add(multiply(ints, 2), 1) {\n    fmt.Println(v)\n}\n```\n[sample](https://github.com/luk4z7/go-concurrency-guide/tree/main/patterns/pipelines)\n\n\n### Fan-in and Fan-out\n\nFan-out is a term to describe the process of starting multiple goroutines to handle pipeline input, and fan-in is a term to describe the process of combining multiple outputs into one channel.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n)\n\ntype data int\n\n// distribute work items to multiple uniform actors\n// no data shall be processed twice!\n// received wch\n// response res\nfunc worker(wch \u003c-chan data, res chan\u003c- data) {\n    for {\n        w, ok := \u003c-wch\n        if !ok {\n            return // return when is closed\n        }\n\n        w *= 2\n        res \u003c- w\n    }\n}\n\nfunc main() {\n    work := []data{1, 2, 3, 4, 5}\n\n    const numWorkers = 3\n\n    wch := make(chan data, len(work))\n    res := make(chan data, len(work))\n\n    // fan-out, one input channel for all actors\n    for i := 0; i \u003c numWorkers; i++ {\n        go worker(wch, res)\n    }\n\n    // fan-out, one input channel for all actors\n    for _, w := range work {\n        fmt.Println(\"send to wch : \", w)\n        wch \u003c- w\n    }\n    close(wch)\n\n    // fan-in, one result channel\n    for range work {\n        w := \u003c-res\n        fmt.Println(\"receive from res : \", w)\n    }\n}\n```\n[sample](https://github.com/luk4z7/go-concurrency-guide/tree/main/patterns/fanoutfanin)\n\n\n### Or done channel\n\nOr done is a way to encapsulate verbosity that can be achieved through for/select breaks to check when a channel has ended, and also avoiding goroutine leakage, the code below could be replaced by a closure that encapsulates that verbosity\n\n```go\nfor val := range myChan {\n    // Do something with val\n}\n\nloop:\nfor {\n    select {\n    case \u003c-done:\n        break loop\n    case maybeVal, ok := \u003c-myChan:\n        if ok == false {\n            return // or maybe break from for\n        }\n        // Do something with val\n    }\n}\n```\n\ncan be created an isolation, a function/method, closure, creating a single goroutine\n\n```go\norDone := func(done, c \u003c-chan interface{}) \u003c-chan interface{} {\n    valStream := make(chan interface{})\n    go func() {\n        defer close(valStream)\n        for {\n            select {\n            case \u003c-done:\n                return\n            case v, ok := \u003c-c:\n                if ok == false {\n                    return\n                }\n                select {\n                case valStream \u003c- v:\n                case \u003c-done:\n                }\n            }\n        }\n    }()\n\n    return valStream\n}\n\nfor val := range orDone(done, myChan) {\n    // Do something with val\n}\n```\n[sample](https://github.com/luk4z7/go-concurrency-guide/tree/main/patterns/ordonechannel)\n\n\n### Tee channel\n\nPass the it a channel to read from, and it will return two separate channels that will get the same value:\n\n```go\ntee := func(done \u003c-chan interface{}, in \u003c-chan interface{}) (_, _ \u003c-chan interface{}) {\n\n    out1 := make(chan interface{})\n    out2 := make(chan interface{})\n\n    go func() {\n        defer close(out1)\n        defer close(out2)\n        for val := range orDone(done, in) {\n            var out1, out2 = out1, out2\n            for i := 0; i \u003c 2; i++ {\n                select {\n                case \u003c-done:\n                case out1 \u003c- val:\n                    out1 = nil\n                case out2 \u003c- val:\n                    out2 = nil\n                }\n            }\n        }\n    }()\n\n    return out1, out2\n}\n```\n[sample](https://github.com/luk4z7/go-concurrency-guide/tree/main/patterns/teechannel)\n\n\n### Bridge channel\n\nWith this patterns is possible to create a function that destruct a channel of channels into a single channel\n\n```go\nbridge := func(done \u003c-chan interface{}, chanStream \u003c-chan \u003c-chan interface{}) \u003c-chan interface{} {\n    valStream := make(chan interface{})\n    go func() {\n        defer close(valStream)\n        for {\n            var stream \u003c-chan interface{}\n            select {\n            case maybeStream, ok := \u003c-chanStream:\n                if ok == false {\n                    return\n                }\n                stream = maybeStream\n\n            case \u003c-done:\n                return\n            }\n\n            for val := range orDone(done, stream) {\n                select {\n                case valStream \u003c- val:\n                case \u003c-done:\n                }\n            }\n        }\n    }()\n\n    return valStream\n}\n\ngenVals := func() \u003c-chan \u003c-chan interface{} {\n    chanStream := make(chan (\u003c-chan interface{}))\n    go func() {\n        defer close(chanStream)\n        for i := 0; i \u003c 10; i++ {\n            stream := make(chan interface{}, 1)\n            stream \u003c- i\n            close(stream)\n            chanStream \u003c- stream\n        }\n    }()\n\n    return chanStream\n}\n\ndone := make(chan interface{})\ndefer close(done)\n\nfor v := range bridge(done, genVals()) {\n    fmt.Printf(\"%v \", v)\n}\n```\n[sample](https://github.com/luk4z7/go-concurrency-guide/tree/main/patterns/bridgechannel)\n\n\n### Queuing\n\nbuffered channel is a type of queue, Adding queuing prematurely can hide synchronization issues such as deadlocks, we can use the queue to make\na limit to processing, in this process when the `limit \u003c- struct{}{}` is full the queue is wait to be released `\u003c-limit`, if we remove them the 50 goroutines are created at the same time\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"runtime\"\n    \"sync\"\n    \"time\"\n)\n\nfunc main() {\n    var wg sync.WaitGroup\n    limit := make(chan interface{}, runtime.NumCPU())\n\n    fmt.Printf(\"Started, Limit %d\\n\", cap(limit))\n\n    workers := func(l chan\u003c- interface{}, wg *sync.WaitGroup) {\n        for i := 0; i \u003c= 50; i++ {\n            i := i\n\n            limit \u003c- struct{}{}\n            wg.Add(1)\n\n            go func(x int, w *sync.WaitGroup) {\n                defer w.Done()\n\n                time.Sleep(1 * time.Second)\n                fmt.Printf(\"Process %d\\n\", i)\n\n                \u003c-limit\n            }(i, wg)\n        }\n    }\n\n    workers(limit, \u0026wg)\n    wg.Wait()\n\n    fmt.Println(\"Finished\")\n}\n```\n[sample](https://github.com/luk4z7/go-concurrency-guide/tree/main/patterns/queuing)\n\n\n### Context package\n\nin concurrent programs it’s often necessary to preempt operations because of timeouts, cancellation, or failure of another portion of the system. We’ve looked at the idiom of creating a done channel, which flows through your program and cancels all blocking concurrent operations. This works well, but it’s also somewhat limited.\n\nIt would be useful if we could communicate extra information alongside the simple notification to cancel: why the cancellation was occuring, or whether or not our function has a deadline by which it needs to complete.\n\nsee below an example to pass value into context, the context package serves two primary purposes: \n- To provide an API for canceling branches of your call-graph.  \n- To provide a data-bag for transporting request-scoped data through your call-graph\n\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n)\n\nfunc main() {\n    ProcessRequest(\"jane\", \"abc123\")\n}\n\nfunc ProcessRequest(userID, authToken string) {\n    ctx := context.WithValue(context.Background(), \"userID\", userID)\n    ctx = context.WithValue(ctx, \"authToken\", authToken)\n    HandleResponse(ctx)\n}\n\nfunc HandleResponse(ctx context.Context) {\n    fmt.Printf(\n        \"handling response for %v (%v)\",\n        ctx.Value(\"userID\"),\n        ctx.Value(\"authToken\"),\n    )\n}\n```\n\nanother example with `Timeout`, cancellation in a function has three aspects:\n\n- A goroutine’s parent may want to cancel it. \n- A goroutine may want to cancel its children.  \n- Any blocking operations within a goroutine need to be preemptable so that it may be canceled.\n\nThe context package helps manage all three of these.\n\n```go\npackage main\n\nimport (\n    \"context\"\n    \"fmt\"\n    \"sync\"\n    \"time\"\n)\n\nfunc main() {\n    var wg sync.WaitGroup\n    ctx, cancel := context.WithCancel(context.Background())\n    defer cancel()\n\n    wg.Add(1)\n    go func() {\n        defer wg.Done()\n\n        if err := printGreeting(ctx); err != nil {\n            fmt.Printf(\"cannot print greeting: %v\\n\", err)\n            cancel()\n        }\n    }()\n\n    wg.Add(1)\n    go func() {\n        defer wg.Done()\n        if err := printFarewell(ctx); err != nil {\n            fmt.Printf(\"cannot print farewell: %v\\n\", err)\n        }\n    }()\n\n    wg.Wait()\n}\n\nfunc printGreeting(ctx context.Context) error {\n    greeting, err := genGreeting(ctx)\n    if err != nil {\n        return err\n    }\n    fmt.Printf(\"%s world!\\n\", greeting)\n\n    return nil\n}\n\nfunc printFarewell(ctx context.Context) error {\n    farewell, err := genFarewell(ctx)\n    if err != nil {\n        return err\n    }\n    fmt.Printf(\"%s world!\\n\", farewell)\n\n    return nil\n}\n\nfunc genGreeting(ctx context.Context) (string, error) {\n    ctx, cancel := context.WithTimeout(ctx, 1*time.Second)\n    defer cancel()\n\n    switch locale, err := locale(ctx); {\n    case err != nil:\n        return \"\", err\n    case locale == \"EN/US\":\n        return \"hello\", nil\n    }\n\n    return \"\", fmt.Errorf(\"unsupported locale\")\n}\n\nfunc genFarewell(ctx context.Context) (string, error) {\n    switch locale, err := locale(ctx); {\n    case err != nil:\n        return \"\", err\n    case locale == \"EN/US\":\n        return \"goodbye\", nil\n    }\n\n    return \"\", fmt.Errorf(\"unsupported locale\")\n}\n\nfunc locale(ctx context.Context) (string, error) {\n    if deadline, ok := ctx.Deadline(); ok {\n        if deadline.Sub(time.Now().Add(1*time.Minute)) \u003c= 0 {\n            return \"\", context.DeadlineExceeded\n        }\n    }\n\n    select {\n    case \u003c-ctx.Done():\n        return \"\", ctx.Err()\n    case \u003c-time.After(1 * time.Minute):\n    }\n\n    return \"EN/US\", nil\n}\n```\n[sample](https://github.com/luk4z7/go-concurrency-guide/tree/main/patterns/contextpackage)\n\n\n### HeartBeats\n\nHeartbeats are a way for concurrent processes to signal life to outside parties. They get their name from human anatomy wherein a heartbeat signifies life to an observer. Heartbeats have been around since before Go, and remain useful within it.\n\nThere are two different types of heartbeats:\n- Heartbeats that occur on a time interval.\n- Heartbeats that occur at the beginning of a unit of work\n\n[sample](https://github.com/luk4z7/go-concurrency-guide/tree/main/patterns/heartbeats)\n\n\n### Replicated Requests\n\nYou should only replicate requests like this to handlers that have different runtime conditions: different processes, machines, paths to a data store, or access to different data stores. While this can be expensive to set up and maintain, if speed is your goal this is a valuable technique. Also, this naturally provides fault tolerance and scalability.\n\nThe only caveat to this approach is that all handlers need to have equal opportunity to fulfill the request. In other words, you won't have a chance to get the fastest time from a handler that can't fulfill the request. As I mentioned, whatever resources the handlers are using to do their work also need to be replicated. A different symptom of the same problem is uniformity. If your handles are very similar, the chances that either one is an outlier are less.\n\n```go\npackage main\n\nimport (\n    \"fmt\"\n    \"math/rand\"\n    \"sync\"\n    \"time\"\n)\n\nfunc main() {\n\n    doWork := func(done \u003c-chan interface{}, id int, wg *sync.WaitGroup, result chan\u003c- int) {\n        started := time.Now()\n        defer wg.Done()\n\n        // Simulate random load\n        simulatedLoadTime := time.Duration(1+rand.Intn(5)) * time.Second\n        select {\n        case \u003c-done:\n        case \u003c-time.After(simulatedLoadTime):\n        }\n\n        select {\n        case \u003c-done:\n        case result \u003c- id:\n        }\n\n        took := time.Since(started)\n        // Display how long handlers would have taken\n        if took \u003c simulatedLoadTime {\n            took = simulatedLoadTime\n\n        }\n\n        fmt.Printf(\"%v took %v\\n\", id, took)\n    }\n\n    done := make(chan interface{})\n    result := make(chan int)\n\n    var wg sync.WaitGroup\n    wg.Add(10)\n\n    // Here we start 10 handlers to handle our requests.\n    for i := 0; i \u003c 10; i++ {\n        go doWork(done, i, \u0026wg, result)\n    }\n\n    // This line grabs the first returned value from the group of handlers.\n    firstReturned := \u003c-result\n\n    // Here we cancel all the remaining handlers.\n    // This ensures they don’t continue to do unnecessary work.\n    close(done)\n    wg.Wait()\n\n    fmt.Printf(\"Received an answer from #%v\\n\", firstReturned)\n}\n```\n[sample](https://github.com/luk4z7/go-concurrency-guide/tree/main/patterns/replicatedrequests)\n\n\n\n## Scheduler Runtime\n\nGo will handle multiplexing goroutines onto OS threads for you.\n\nThe algorithm it uses to do this is known as a work `stealing strategy`.\n\nfair scheduling. In an effort to ensure all processors were equally utilized, we could evenly distribute the load between all available processors. Imagine there are n processors and x tasks to perform. In the fair scheduling strategy, each processor would get x/n tasks:\n\nGo models concurrency using a fork-join model.\n\nAs a refresher, remember that Go follows a fork-join model for concurrency. Forks are when goroutines are started, and join points are when two or more goroutines are synchronized through channels or types in the sync package. The work stealing algorithm follows a few basic rules. Given a thread of execution:\n\nAt a fork point, add tasks to the tail of the deque associated with the thread.\n\n\nGo scheduler’s job is to distribute runnable goroutines over multiple worker OS threads that runs on one or more processors. In multi-threaded computation, two paradigms have emerged in scheduling: work sharing and work stealing.\n\n- Work-sharing: When a processor generates new threads, it attempts to migrate some of them to the other processors with the hopes of them being utilized by the idle/underutilized processors.\n- Work-stealing: An underutilized processor actively looks for other processor’s threads and “steal” some.\n\nThe migration of threads occurs less frequently with work stealing than with work sharing. When all processors have work to run, no threads are being migrated. And as soon as there is an idle processor, migration is considered.\n\nGo has a work-stealing scheduler since 1.1, contributed by Dmitry Vyukov. This article will go in depth explaining what work-stealing schedulers are and how Go implements one.\n\n\n**Scheduling basics**\n\nGo has an M:N scheduler that can also utilize multiple processors. At any time, M goroutines need to be scheduled on N OS threads that runs on at most GOMAXPROCS numbers of processors. Go scheduler uses the following terminology for goroutines, threads and processors:\n\n- G: goroutine\u003cbr/\u003e\n- M: OS thread (machine)\u003cbr/\u003e\n- P: processor\u003cbr/\u003e\n\nThere is a P-specific local and a global goroutine queue. Each M should be assigned to a P. Ps may have no Ms if they are blocked or in a system call. At any time, there are at most GOMAXPROCS number of P. At any time, only one M can run per P. More Ms can be created by the scheduler if required.\n[runtime doc](https://github.com/golang/go/blob/master/src/runtime/proc.go)\n\n\n**Why have a scheduler?**\n\ngoroutines are user-space threads\nconceptually similar to kernel threads managed by the OS, but managed entirely by the Go runtime\n\nlighter-weight  and cheaper than kernel threads.\n\n* smaller memory footprint:\n    * initial goroutine stack = 2KB; default thread stack = 8KB\n    * state tracking overhead\n    * faster creation, destruction, context switchesL\n    * goroutines switches = ~tens of ns; thread switches = ~ a us.\n\nGo schedule put her  goroutines on kernel threads which run on the CPU\n\n\n\n### References:\n\n\n[Go Programming Language](https://www.gopl.io)\n\n\n[Go Concurrency in Go](https://katherine.cox-buday.com/concurrency-in-go)\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluk4z7%2Fgo-concurrency-guide","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fluk4z7%2Fgo-concurrency-guide","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fluk4z7%2Fgo-concurrency-guide/lists"}