{"id":1067,"date":"2026-08-16T21:44:06","date_gmt":"2026-08-16T21:44:06","guid":{"rendered":"https:\/\/www.devopsschool.com\/tutorials\/?p=1067"},"modified":"2026-08-17T08:33:13","modified_gmt":"2026-08-17T08:33:13","slug":"go-tutorials-go-concurrency-beginner-tutorial","status":"publish","type":"post","link":"https:\/\/www.devopsschool.com\/tutorials\/go-tutorials-go-concurrency-beginner-tutorial\/","title":{"rendered":"Go Tutorials &#8211; Go Concurrency Beginner Tutorial"},"content":{"rendered":"\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"909\" height=\"1024\" src=\"https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-2-909x1024.png\" alt=\"\" class=\"wp-image-1110\" srcset=\"https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-2-909x1024.png 909w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-2-266x300.png 266w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-2-768x866.png 768w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-2.png 1181w\" sizes=\"auto, (max-width: 909px) 100vw, 909px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Go concurrency becomes much easier when you separate these ideas:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Concurrency\n    \u2193\nGoroutines = workers doing work concurrently\n    \u2193\nChannels = communication path between goroutines\n    \u2193\nMessages = values sent through channels\n    \u2193\nWaitGroup = waits until goroutines finish<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">A useful mental model is a <strong>restaurant kitchen<\/strong>:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Go concept<\/th><th>Simple analogy<\/th><\/tr><\/thead><tbody><tr><td>Concurrency<\/td><td>Several orders being handled at the same time<\/td><\/tr><tr><td>Goroutine<\/td><td>One cook<\/td><\/tr><tr><td>Worker<\/td><td>A cook assigned to process jobs<\/td><\/tr><tr><td>Channel<\/td><td>Counter\/conveyor where jobs are placed<\/td><\/tr><tr><td>Message<\/td><td>The actual order placed on the counter<\/td><\/tr><tr><td>WaitGroup<\/td><td>Manager waiting for all cooks to finish<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<h1 class=\"wp-block-heading\">1. Concurrency<\/h1>\n\n\n\n<h2 class=\"wp-block-heading\">What is concurrency?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Concurrency means:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Multiple tasks can make progress during the same period of time.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">In Go, concurrency is mainly implemented using <strong>goroutines<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Suppose you have three jobs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Job 1 \u2192 2 seconds\nJob 2 \u2192 2 seconds\nJob 3 \u2192 2 seconds<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Without concurrency:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Job1 \u2192 Job2 \u2192 Job3\n\n2 sec + 2 sec + 2 sec = ~6 seconds<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">With concurrency:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Job1 \u2500\u2500\u2500\u2500\u2500\u2510\nJob2 \u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500 running together\nJob3 \u2500\u2500\u2500\u2500\u2500\u2518\n\n\u2248 2 seconds<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Why?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Concurrency is useful when your program spends time waiting for things such as:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>APIs<\/li>\n\n\n\n<li>databases<\/li>\n\n\n\n<li>files<\/li>\n\n\n\n<li>network requests<\/li>\n\n\n\n<li>timers<\/li>\n\n\n\n<li>user requests<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">When?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Use concurrency when multiple independent operations can happen at the same time.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">How?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Most commonly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go someFunction()<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Where?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Very common in:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Web Servers\nAPIs\nMicroservices\nNetwork applications\nBackground processing\nWorker systems\nFile processing<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Complete Example \u2014 Concurrency<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Save as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>main.go<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\nfunc task(name string) {\n\n\tfmt.Println(name, \"started\")\n\n\t\/\/ Simulate some work\n\ttime.Sleep(2 * time.Second)\n\n\tfmt.Println(name, \"finished\")\n}\n\nfunc main() {\n\n\t\/\/ Start three tasks concurrently.\n\tgo task(\"Task 1\")\n\tgo task(\"Task 2\")\n\tgo task(\"Task 3\")\n\n\t\/\/ Keep main alive long enough for this simple demo.\n\ttime.Sleep(3 * time.Second)\n\n\tfmt.Println(\"Program finished\")\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go run main.go<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Possible output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Task 3 started\nTask 1 started\nTask 2 started\nTask 2 finished\nTask 1 finished\nTask 3 finished\nProgram finished<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Notice that the order is <strong>not guaranteed<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is normal in concurrent programming.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"682\" src=\"https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-1024x682.png\" alt=\"\" class=\"wp-image-1106\" srcset=\"https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-1024x682.png 1024w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-300x200.png 300w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-768x512.png 768w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image.png 1400w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h1 class=\"wp-block-heading\">Important<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>task(\"Task 1\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">means:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Run the function normally and wait for it to finish.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">This:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go task(\"Task 1\")<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">means:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Start <code>task()<\/code> as a goroutine and continue without waiting for it.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">2. Goroutine \u2014 Concurrent Worker<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A <strong>goroutine<\/strong> is a lightweight concurrent function managed by Go.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">You can think:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>function + go keyword = goroutine<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>worker()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Normal function call.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go worker()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">starts it concurrently.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">What?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A goroutine is a function running independently\/concurrently with other goroutines.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Why?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">It lets multiple pieces of work progress without forcing one to completely finish before another starts.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">When?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Use it when tasks are independent.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Examples:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Call API A\nCall API B\nCall API C<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Instead of waiting:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>A \u2192 B \u2192 C<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">they could run:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>A \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nB \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nC \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\">How?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Simply add:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">before a function call.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Where?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Common places include:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>HTTP servers<\/li>\n\n\n\n<li>API calls<\/li>\n\n\n\n<li>database operations<\/li>\n\n\n\n<li>background jobs<\/li>\n\n\n\n<li>queues<\/li>\n\n\n\n<li>worker pools<\/li>\n<\/ul>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Complete Example \u2014 Goroutine Worker<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\n\/\/ worker represents some work we want done.\nfunc worker(id int) {\n\n\tfmt.Println(\"Worker\", id, \"started\")\n\n\ttime.Sleep(2 * time.Second)\n\n\tfmt.Println(\"Worker\", id, \"finished\")\n}\n\nfunc main() {\n\n\t\/\/ Each worker runs as its own goroutine.\n\tgo worker(1)\n\tgo worker(2)\n\tgo worker(3)\n\n\t\/\/ Only for this basic example.\n\ttime.Sleep(3 * time.Second)\n\n\tfmt.Println(\"All done\")\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The important lines are:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go worker(1)\ngo worker(2)\ngo worker(3)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">They create three goroutines.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Conceptually:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;\"main()\"] --&gt; B&#91;\"go worker(1)\"]\n    A --&gt; C&#91;\"go worker(2)\"]\n    A --&gt; D&#91;\"go worker(3)\"]\n\n    B --&gt; E&#91;\"Worker 1\"]\n    C --&gt; F&#91;\"Worker 2\"]\n    D --&gt; G&#91;\"Worker 3\"]<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Function vs Goroutine<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Normal function<\/th><th>Goroutine<\/th><\/tr><\/thead><tbody><tr><td><code>worker()<\/code><\/td><td><code>go worker()<\/code><\/td><\/tr><tr><td>Caller waits<\/td><td>Caller continues<\/td><\/tr><tr><td>Sequential<\/td><td>Concurrent<\/td><\/tr><tr><td>One completes before next statement<\/td><td>Work can overlap<\/td><\/tr><tr><td>Simple execution<\/td><td>Concurrent execution<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>worker(1)\nworker(2)\nworker(3)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">means:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Worker 1\n   \u2193\nWorker 2\n   \u2193\nWorker 3<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">But:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go worker(1)\ngo worker(2)\ngo worker(3)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">means approximately:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Worker 1 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nWorker 2 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nWorker 3 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">3. Worker + Channel + Message<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This is one of the most important Go concurrency patterns.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">There are three pieces:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Worker\nChannel\nMessage<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Think about it like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Message = work that needs to be done\n\nChannel = path used to send the work\n\nWorker = goroutine receiving and processing the work<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Jobs\n  \u2193\nChannel\n  \u2193\nWorker<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">What is a worker?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A worker is usually a goroutine that waits for jobs.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>func worker(jobs chan string)<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">What is a channel?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A channel lets goroutines safely communicate.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Create one:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs := make(chan string)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Send data:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs &lt;- \"Job 1\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Receive data:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>job := &lt;-jobs<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The arrow tells you the direction the value travels.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">What is a message?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">A message is simply the data flowing through the channel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">It could be:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>string\nint\nstruct\nerror<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs &lt;- \"Process customer 1001\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Here:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Channel = jobs\nMessage = \"Process customer 1001\"<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Complete Example \u2014 Worker + Channel + Message<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"fmt\"\n\t\"time\"\n)\n\n\/\/ Worker receives messages from the jobs channel.\nfunc worker(jobs chan string) {\n\n\t\/\/ Keep receiving messages until the channel is closed.\n\tfor job := range jobs {\n\n\t\tfmt.Println(\"Worker received:\", job)\n\n\t\ttime.Sleep(1 * time.Second)\n\n\t\tfmt.Println(\"Worker completed:\", job)\n\t}\n}\n\nfunc main() {\n\n\t\/\/ Create a channel that carries strings.\n\tjobs := make(chan string)\n\n\t\/\/ Start worker as a goroutine.\n\tgo worker(jobs)\n\n\t\/\/ Send messages into the channel.\n\tjobs &lt;- \"Job 1\"\n\tjobs &lt;- \"Job 2\"\n\tjobs &lt;- \"Job 3\"\n\n\t\/\/ No more jobs will be sent.\n\tclose(jobs)\n\n\t\/\/ Only for this basic example.\n\ttime.Sleep(4 * time.Second)\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The important part:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs := make(chan string)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">creates the channel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs &lt;- \"Job 1\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">sends a message.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">And:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>for job := range jobs<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">receives messages.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h2 class=\"wp-block-heading\">Visualization<\/h2>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;\"main()\"] --&gt;|\"Job 1\"| C&#91;\"jobs channel\"]\n    A --&gt;|\"Job 2\"| C\n    A --&gt;|\"Job 3\"| C\n\n    C --&gt; W&#91;\"Worker Goroutine\"]\n\n    W --&gt; R&#91;\"Process Job\"]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Think:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>main\n \u2502\n \u2502 sends jobs\n \u25bc\n+-------------+\n|   channel   |\n+-------------+\n      \u2502\n      \u2502 receives\n      \u25bc\n+-------------+\n|   worker    |\n+-------------+<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Channel Syntax Cheat Sheet<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Operation<\/th><th>Code<\/th><\/tr><\/thead><tbody><tr><td>Create channel<\/td><td><code>make(chan string)<\/code><\/td><\/tr><tr><td>Send<\/td><td><code>ch &lt;- value<\/code><\/td><\/tr><tr><td>Receive<\/td><td><code>value := &lt;-ch<\/code><\/td><\/tr><tr><td>Close<\/td><td><code>close(ch)<\/code><\/td><\/tr><tr><td>Read until closed<\/td><td><code>for value := range ch<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The easiest way to remember:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>value goes toward the arrow<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Send:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs &lt;- \"hello\"<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs \u2190 \"hello\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Receive:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>message := &lt;-jobs<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>message \u2190 jobs<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">4. Without Goroutine vs With Goroutine \u2014 Performance<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This is probably the best experiment for understanding <strong>why goroutines exist<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We will execute 5 tasks.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Every task takes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1 second<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Sequential execution should therefore take roughly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>5 \u00d7 1 second\n=\n5 seconds<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Concurrent execution should take roughly:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1 second<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">because the simulated waiting overlaps.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Complete Example \u2014 Performance Comparison<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Copy-paste this entire program:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n)\n\nfunc work(id int) {\n\n\tfmt.Println(\"Working on job\", id)\n\n\t\/\/ Simulate slow work such as API\/database\/network calls.\n\ttime.Sleep(1 * time.Second)\n}\n\nfunc main() {\n\n\t\/\/ --------------------------------\n\t\/\/ WITHOUT GOROUTINES\n\t\/\/ --------------------------------\n\n\tfmt.Println(\"WITHOUT GOROUTINES\")\n\n\tstart := time.Now()\n\n\tfor i := 1; i &lt;= 5; i++ {\n\t\twork(i)\n\t}\n\n\tfmt.Println(\n\t\t\"Sequential time:\",\n\t\ttime.Since(start),\n\t)\n\n\t\/\/ --------------------------------\n\t\/\/ WITH GOROUTINES\n\t\/\/ --------------------------------\n\n\tfmt.Println(\"\\nWITH GOROUTINES\")\n\n\tstart = time.Now()\n\n\tvar wg sync.WaitGroup\n\n\tfor i := 1; i &lt;= 5; i++ {\n\n\t\twg.Add(1)\n\n\t\tgo func(id int) {\n\n\t\t\tdefer wg.Done()\n\n\t\t\twork(id)\n\n\t\t}(i)\n\t}\n\n\t\/\/ Wait until all goroutines finish.\n\twg.Wait()\n\n\tfmt.Println(\n\t\t\"Concurrent time:\",\n\t\ttime.Since(start),\n\t)\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Typical output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>WITHOUT GOROUTINES\n\nWorking on job 1\nWorking on job 2\nWorking on job 3\nWorking on job 4\nWorking on job 5\n\nSequential time: 5.00s\n\n\nWITH GOROUTINES\n\nWorking on job 5\nWorking on job 2\nWorking on job 1\nWorking on job 4\nWorking on job 3\n\nConcurrent time: 1.00s<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Why?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Sequential:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;\"Job 1&lt;br\/&gt;1 sec\"] --&gt; B&#91;\"Job 2&lt;br\/&gt;1 sec\"]\n    B --&gt; C&#91;\"Job 3&lt;br\/&gt;1 sec\"]\n    C --&gt; D&#91;\"Job 4&lt;br\/&gt;1 sec\"]\n    D --&gt; E&#91;\"Job 5&lt;br\/&gt;1 sec\"]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Total:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1 + 1 + 1 + 1 + 1\n\u2248 5 seconds<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Concurrent:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    A&#91;\"main()\"] --&gt; B&#91;\"Job 1\"]\n    A --&gt; C&#91;\"Job 2\"]\n    A --&gt; D&#91;\"Job 3\"]\n    A --&gt; E&#91;\"Job 4\"]\n    A --&gt; F&#91;\"Job 5\"]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">All five wait concurrently:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Job 1 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 1 sec\nJob 2 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 1 sec\nJob 3 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 1 sec\nJob 4 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 1 sec\nJob 5 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 1 sec\n\nTotal \u2248 1 second<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Very Important: Goroutine Doesn&#8217;t Automatically Mean Faster<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Our example uses:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>time.Sleep()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">to simulate operations such as:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>API call\nDatabase request\nNetwork request\nDisk operation<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">These are excellent candidates for concurrency because much of the time is spent <strong>waiting<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">But:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>goroutine = faster<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">is <strong>not always true<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For tiny CPU operations, creating lots of goroutines can actually add overhead.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">So think:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>Use concurrency when work can meaningfully overlap, not merely because goroutines exist.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">5. WaitGroup<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">You probably noticed a problem with our earlier examples.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">We did this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>time.Sleep(3 * time.Second)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That is not a good way for a real program to wait for workers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">How do we know that 3 seconds is enough?<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Maybe the worker needs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1 second\n5 seconds\n20 seconds<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We shouldn&#8217;t guess.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">That is why Go provides:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sync.WaitGroup<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">What?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">A WaitGroup waits for a group of goroutines to finish.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Why?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Because <code>main()<\/code> does <strong>not automatically wait<\/strong> for other goroutines.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Consider:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>func main() {\n\n\tgo worker()\n\n\tfmt.Println(\"Done\")\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>main()<\/code> might finish before <code>worker()<\/code> completes.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When <code>main()<\/code> ends:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>program ends<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">and remaining goroutines disappear.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">When?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Use WaitGroup when:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">You start multiple goroutines and need to wait for all of them to complete.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">How?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Three main operations:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wg.Add(1)\nwg.Done()\nwg.Wait()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Think:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Add()  = one worker started\nDone() = one worker finished\nWait() = wait until counter becomes zero<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Complete Example \u2014 WaitGroup<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n)\n\nfunc worker(id int, wg *sync.WaitGroup) {\n\n\t\/\/ Tell WaitGroup this worker is finished\n\t\/\/ when this function returns.\n\tdefer wg.Done()\n\n\tfmt.Println(\"Worker\", id, \"started\")\n\n\ttime.Sleep(2 * time.Second)\n\n\tfmt.Println(\"Worker\", id, \"finished\")\n}\n\nfunc main() {\n\n\tvar wg sync.WaitGroup\n\n\tfor i := 1; i &lt;= 3; i++ {\n\n\t\t\/\/ One more goroutine will start.\n\t\twg.Add(1)\n\n\t\tgo worker(i, &amp;wg)\n\t}\n\n\tfmt.Println(\"Waiting for workers...\")\n\n\t\/\/ Block here until all workers call Done().\n\twg.Wait()\n\n\tfmt.Println(\"All workers completed\")\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Possible output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Waiting for workers...\nWorker 3 started\nWorker 1 started\nWorker 2 started\nWorker 2 finished\nWorker 1 finished\nWorker 3 finished\nAll workers completed<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Understand WaitGroup Counter<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Initially:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>counter = 0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wg.Add(1)\nwg.Add(1)\nwg.Add(1)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Counter becomes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>3<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Worker 1:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wg.Done()<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>3 \u2192 2<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Worker 2:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>2 \u2192 1<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Worker 3:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1 \u2192 0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wg.Wait()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">can continue.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n    A&#91;\"WaitGroup = 0\"] --&gt; B&#91;\"Add(1)&lt;br\/&gt;counter = 1\"]\n    B --&gt; C&#91;\"Add(1)&lt;br\/&gt;counter = 2\"]\n    C --&gt; D&#91;\"Add(1)&lt;br\/&gt;counter = 3\"]\n\n    D --&gt; E&#91;\"Worker 1 Done&lt;br\/&gt;counter = 2\"]\n    E --&gt; F&#91;\"Worker 2 Done&lt;br\/&gt;counter = 1\"]\n    F --&gt; G&#91;\"Worker 3 Done&lt;br\/&gt;counter = 0\"]\n\n    G --&gt; H&#91;\"Wait() released\"]<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Why <code>defer wg.Done()<\/code>?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">You will very commonly see:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>defer wg.Done()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">instead of:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wg.Done()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">at the bottom.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><code>defer<\/code> means:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Run this when the function is about to return.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">So:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>func worker() {\n\n\tdefer wg.Done()\n\n\t\/\/ work...\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">means:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>worker starts\n    \u2193\ndo work\n    \u2193\nfunction finishes\n    \u2193\nwg.Done()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">It makes it much harder to accidentally forget to mark the worker finished.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Putting Everything Together<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Now let&#8217;s connect every concept.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Imagine this program:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>10 image-processing jobs<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">We create:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>3 workers<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The architecture could be:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart LR\n    M&#91;\"main()\"]\n\n    M --&gt;|\"Job 1\"| C&#91;\"jobs channel\"]\n    M --&gt;|\"Job 2\"| C\n    M --&gt;|\"Job 3\"| C\n    M --&gt;|\"...\"| C\n\n    C --&gt; W1&#91;\"Worker 1&lt;br\/&gt;goroutine\"]\n    C --&gt; W2&#91;\"Worker 2&lt;br\/&gt;goroutine\"]\n    C --&gt; W3&#91;\"Worker 3&lt;br\/&gt;goroutine\"]\n\n    W1 --&gt; WG&#91;\"WaitGroup\"]\n    W2 --&gt; WG\n    W3 --&gt; WG\n\n    WG --&gt; END&#91;\"Program finishes\"]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This is the core idea behind many Go systems.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Complete Worker Pool Example<\/h1>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"683\" height=\"1024\" src=\"https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-3-683x1024.png\" alt=\"\" class=\"wp-image-1111\" srcset=\"https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-3-683x1024.png 683w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-3-200x300.png 200w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-3-768x1152.png 768w, https:\/\/www.devopsschool.com\/tutorials\/wp-content\/uploads\/2026\/08\/image-3.png 1024w\" sizes=\"auto, (max-width: 683px) 100vw, 683px\" \/><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">This section combines everything into one practical example.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>package main\n\nimport (\n\t\"fmt\"\n\t\"sync\"\n\t\"time\"\n)\n\n\/\/ worker receives jobs from the jobs channel.\nfunc worker(\n\tid int,\n\tjobs &lt;-chan int,\n\twg *sync.WaitGroup,\n) {\n\n\tdefer wg.Done()\n\n\t\/\/ Continue until jobs channel is closed.\n\tfor job := range jobs {\n\n\t\tfmt.Println(\n\t\t\t\"Worker\", id,\n\t\t\t\"processing job\", job,\n\t\t)\n\n\t\ttime.Sleep(1 * time.Second)\n\t}\n\n\tfmt.Println(\"Worker\", id, \"finished\")\n}\n\nfunc main() {\n\n\t\/\/ Channel containing jobs\/messages.\n\tjobs := make(chan int)\n\n\tvar wg sync.WaitGroup\n\n\t\/\/ Start 3 workers.\n\tfor i := 1; i &lt;= 3; i++ {\n\n\t\twg.Add(1)\n\n\t\tgo worker(i, jobs, &amp;wg)\n\t}\n\n\t\/\/ Send 6 jobs.\n\tfor job := 1; job &lt;= 6; job++ {\n\t\tjobs &lt;- job\n\t}\n\n\t\/\/ Tell workers there are no more jobs.\n\tclose(jobs)\n\n\t\/\/ Wait for workers to finish.\n\twg.Wait()\n\n\tfmt.Println(\"All jobs completed\")\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Possible output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Worker 3 processing job 1\nWorker 1 processing job 2\nWorker 2 processing job 3\n\nWorker 2 processing job 4\nWorker 3 processing job 5\nWorker 1 processing job 6\n\nWorker 1 finished\nWorker 2 finished\nWorker 3 finished\n\nAll jobs completed<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This example is extremely important because it represents a real <strong>worker pool<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">What Exactly Happened?<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">First:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs := make(chan int)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">creates the job channel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Then:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go worker(1, jobs, &amp;wg)\ngo worker(2, jobs, &amp;wg)\ngo worker(3, jobs, &amp;wg)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">conceptually creates three workers.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">They are all waiting here:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>for job := range jobs<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Then <code>main()<\/code> sends:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>jobs &lt;- 1\njobs &lt;- 2\njobs &lt;- 3<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Workers receive them.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Conceptually:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>                    jobs channel\n\nJob 1 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2192 Worker 1\nJob 2 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2192 Worker 2\nJob 3 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2192 Worker 3\nJob 4 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2192 whichever worker becomes free\nJob 5 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2192 whichever worker becomes free\nJob 6 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2192 whichever worker becomes free<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Finally:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>close(jobs)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">means:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">No more jobs are coming.<\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">And:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>wg.Wait()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">means:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\">Don&#8217;t finish <code>main()<\/code> until every worker finishes.<\/p>\n<\/blockquote>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Worker vs Goroutine<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">These terms are related but are <strong>not exactly the same thing<\/strong>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A goroutine is a Go language\/runtime mechanism.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">A worker is a programming design concept.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">For example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go worker()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Here:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>worker()\n    =\nfunction containing worker logic\n\ngo worker()\n    =\nworker running inside a goroutine<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>A worker is usually implemented using a goroutine.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\">But every goroutine is not necessarily called a worker.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go logSomething()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That&#8217;s a goroutine, but you probably wouldn&#8217;t describe it as a worker pool worker.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Worker vs Channel vs Message vs WaitGroup<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">This is the comparison worth remembering.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Concept<\/th><th>What is it?<\/th><th>Main purpose<\/th><th>Example<\/th><\/tr><\/thead><tbody><tr><td>Concurrency<\/td><td>Overall programming model<\/td><td>Multiple tasks progress together<\/td><td>Many API calls<\/td><\/tr><tr><td>Goroutine<\/td><td>Lightweight concurrent execution<\/td><td>Run a function concurrently<\/td><td><code>go worker()<\/code><\/td><\/tr><tr><td>Worker<\/td><td>Function\/goroutine processing jobs<\/td><td>Perform work<\/td><td><code>worker(1)<\/code><\/td><\/tr><tr><td>Channel<\/td><td>Communication pipe<\/td><td>Transfer data between goroutines<\/td><td><code>jobs := make(chan int)<\/code><\/td><\/tr><tr><td>Message<\/td><td>Data sent through channel<\/td><td>Represent job\/event\/data<\/td><td><code>jobs &lt;- 10<\/code><\/td><\/tr><tr><td>WaitGroup<\/td><td>Synchronization mechanism<\/td><td>Wait for goroutines<\/td><td><code>wg.Wait()<\/code><\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Exact Relationship<\/h1>\n\n\n\n<pre class=\"wp-block-code\"><code>flowchart TD\n\n    C&#91;\"Concurrency\"]\n\n    C --&gt; G&#91;\"Goroutines\"]\n\n    G --&gt; W1&#91;\"Worker 1\"]\n    G --&gt; W2&#91;\"Worker 2\"]\n    G --&gt; W3&#91;\"Worker 3\"]\n\n    CH&#91;\"Channel\"] --&gt;|\"Messages \/ Jobs\"| W1\n    CH --&gt;|\"Messages \/ Jobs\"| W2\n    CH --&gt;|\"Messages \/ Jobs\"| W3\n\n    W1 --&gt; WG&#91;\"WaitGroup\"]\n    W2 --&gt; WG\n    W3 --&gt; WG\n\n    WG --&gt; M&#91;\"main() continues\"]<\/code><\/pre>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">The Five Things You Should Remember<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">If everything else feels confusing, remember these five lines:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ 1. Start something concurrently\ngo worker()<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ 2. Create communication pipe\njobs := make(chan int)<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ 3. Send message\njobs &lt;- 100<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ 4. Receive message\njob := &lt;-jobs<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>\/\/ 5. Wait for goroutines\nwg.Wait()<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">That is most of the foundation.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">One Mental Model for Everything<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Think about an <strong>Amazon warehouse<\/strong>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Orders\n   \u2193\n+-------------------+\n|   Conveyor Belt   |  \u2190 Channel\n+-------------------+\n   \u2193      \u2193      \u2193\nWorker  Worker  Worker\n   1       2       3\n   \u2193       \u2193       \u2193\nProcess Process Process\n Orders  Orders  Orders\n   \u2193       \u2193       \u2193\n      WaitGroup\n          \u2193\n     All finished<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Mapping:<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Warehouse<\/th><th>Go<\/th><\/tr><\/thead><tbody><tr><td>Warehouse operation<\/td><td>Concurrency<\/td><\/tr><tr><td>Employee<\/td><td>Worker<\/td><\/tr><tr><td>Employee working independently<\/td><td>Goroutine<\/td><\/tr><tr><td>Conveyor belt<\/td><td>Channel<\/td><\/tr><tr><td>Package\/order<\/td><td>Message<\/td><\/tr><tr><td>Supervisor waiting for everyone<\/td><td>WaitGroup<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">If this picture is clear, Go concurrency becomes much easier.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Sequential vs Concurrent vs Worker Pool<\/h1>\n\n\n\n<p class=\"wp-block-paragraph\">Finally, distinguish these three architectures.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Sequential<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Job 1 \u2192 Job 2 \u2192 Job 3 \u2192 Job 4<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>worker(1)\nworker(2)\nworker(3)\nworker(4)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">One after another.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Goroutine per job<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>Job 1 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nJob 2 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nJob 3 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\nJob 4 \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Code:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>go worker(1)\ngo worker(2)\ngo worker(3)\ngo worker(4)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Potentially many concurrent goroutines.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Worker pool<\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code>                    \u250c\u2500\u2500 Worker 1\nJobs \u2192 Channel \u2500\u2500\u2500\u2500\u2500\u253c\u2500\u2500 Worker 2\n                    \u2514\u2500\u2500 Worker 3<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Even if there are:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>10,000 jobs<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">you might deliberately have only:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>10 workers<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">This lets you <strong>control concurrency<\/strong>.<\/p>\n\n\n\n<hr class=\"wp-block-separator has-alpha-channel-opacity\"\/>\n\n\n\n<h1 class=\"wp-block-heading\">Final Comparison<\/h1>\n\n\n\n<figure class=\"wp-block-table\"><table class=\"has-fixed-layout\"><thead><tr><th>Question<\/th><th>Sequential<\/th><th>Goroutines<\/th><th>Worker Pool<\/th><\/tr><\/thead><tbody><tr><td>Concurrent?<\/td><td>\u274c<\/td><td>\u2705<\/td><td>\u2705<\/td><\/tr><tr><td>Uses <code>go<\/code>?<\/td><td>\u274c<\/td><td>\u2705<\/td><td>\u2705<\/td><\/tr><tr><td>Uses channel?<\/td><td>Usually no<\/td><td>Not required<\/td><td>Usually yes<\/td><\/tr><tr><td>Number of workers controlled?<\/td><td>1<\/td><td>Often one\/job<\/td><td>\u2705 Yes<\/td><\/tr><tr><td>Good for few independent jobs?<\/td><td>Sometimes<\/td><td>\u2705 Excellent<\/td><td>Possible<\/td><\/tr><tr><td>Good for thousands of jobs?<\/td><td>Slow<\/td><td>Can create too much concurrency<\/td><td>\u2705 Excellent<\/td><\/tr><tr><td>Complexity<\/td><td>Lowest<\/td><td>Low<\/td><td>Medium<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">The learning order I recommend is:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>1. Normal function\n       \u2193\n2. Goroutine\n       \u2193\n3. WaitGroup\n       \u2193\n4. Channel\n       \u2193\n5. Worker + Channel\n       \u2193\n6. Worker Pool<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The single most important sentence is:<\/p>\n\n\n\n<blockquote class=\"wp-block-quote is-layout-flow wp-block-quote-is-layout-flow\">\n<p class=\"wp-block-paragraph\"><strong>A goroutine performs concurrent work, a channel communicates between goroutines, a message is the data traveling through that channel, and a WaitGroup waits for goroutines to finish.<\/strong><\/p>\n<\/blockquote>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Go concurrency becomes much easier when you separate these ideas: A useful mental model is a restaurant kitchen: Go concept Simple analogy Concurrency Several orders being handled&#8230; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1067","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1067","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/comments?post=1067"}],"version-history":[{"count":3,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1067\/revisions"}],"predecessor-version":[{"id":1112,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/posts\/1067\/revisions\/1112"}],"wp:attachment":[{"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/media?parent=1067"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/categories?post=1067"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.devopsschool.com\/tutorials\/wp-json\/wp\/v2\/tags?post=1067"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}