{"action":"created","comment":{"url":"https://api.github.com/repos/unacast/unacast.github.io/pulls/comments/69082533","id":69082533,"diff_hunk":"@@ -0,0 +1,69 @@\n+---\n+layout: post\n+title: \"Introduction to concurrency in Go\"\n+ghavatar: 310118\n+ghname: gronnbeck\n+jobtitle: Platform Engineer\n+tags: [go, golang, concurrency]\n+---\n+\n+## Introduction\n+Today, I’m writing about concurrency and concurrency patterns in Go. In this blog post I will outline why I think concurrency is important and how they can be implemented in Go using channels and goroutines.\n+\n+Disclaimer: This post is heavily is inspired by [“Go Concurrency Patterns”](https://www.youtube.com/watch?time_continue=3&v=f6kdp27TYZs) a talk by Rob Pike.\n+\n+## Why is concurrency important?\n+Web services today is largely dependent upon I/O. Either from disk, database or an external service. Running these operations sequentially and waiting for them to finish will result in a slow and underperforming system. Most modern web frameworks solves the basic issues for you. That is, without setup it handles each http request concurrently. But if you need to do something out of the ordinary, like calling a few external services and combined the results you are mostly on your own.\n+\n+The two most common models for concurrency that I’ve used is shared-memory model using Threads like in Java. Or callbacks used in asynchronously languages like in Node.js. I believe that both approaches can be insanely powerful when done right. However, that they’re also insanely hard to get right. Shared-memory model sharing state/messages through memory using locks and is error-prone to say the least. And asynchronous programming is, at least in my experience, a hard programming paradigm reason about and especially to master.\n+\n+## Concurrency in Go\n+Go solves concurrency in a different manner. It’s similar to Threads but instead of sharing messages through memory, go shares memory through messages. Go uses goroutines to achieve concurrency and channels for passing data between them. We will dig into these two concepts a bit further.\n+\n+### Goroutines\n+\n+Goroutines is a simple abstraction for running things (functions) concurrently.  This is achieved by prepending ``go`` before a function call. E.g.\n+\n+<script src=\"https://gist.github.com/gronnbeck/9c363b773e7bb43b4e58cea67ba8cb89.js\"></script>\n+\n+A good example of the concept can be found [here](https://tour.golang.org/concurrency/1)\n+\n+### Channels\n+\n+Channels is the construct for passing data between routines in Go. A channel blocks both on the sending and receiving side until both are ready. Meaning a channel can be used for both synchronising goroutines and passing data between them. Below we see a simple example of how to use channels in Go. The basic idea is that data flows the same directions as the arrow.\n+\n+<script src=\"https://gist.github.com/gronnbeck/9c363b773e7bb43b4e58cea67ba8cb89.js\"></script>\n+\n+In the example below we see how channels and goroutines can be used to create a function utilising concurrency that is easy to understand and reason about.\n+\n+## Example: using goroutines and channels\n+\n+First let’s assume we want to create service that asks three external services and return them. Let’s call these three services Facebook, Twitter and Github. For simplicity, we a fake communication with each of these services, such that the result of each service can be found at ``https://localhost/{facebook, twitter, github}``, respectively.\n+\n+The behaviour for ``GetAllTheThings`` is to fetch data from all services defined and combined them into a list. Let’s start with a naive approach.\n+\n+<script src=\"https://gist.github.com/gronnbeck/6caa0bac97e217e9542d41ab35398da4.js\"></script>\n+\n+Above we see an example implementation of the naive approach. In other words we query each service sequentially. That means that the call to the Github service has to wait for the Facebook service. And the Twitter service needs to wait on both Github and Facebook. Since each of these services are not dependent on each other. We can improve this by performing the requests concurrently. Enter channels and goroutines.\n+\n+<script src=\"https://gist.github.com/gronnbeck/4feddc7018cd917aceea0e2d471bc978.js\"></script>\n+\n+We’ve now modified the naive approach using channels and goroutines. We see that each call is being issued inside a goroutine. And that the results are being collected in the for-loop ant the end. The code can be read sequentially and therefore easy to reason about. It’s also explicitly concurrent since we explicitly issue several goroutines. The only caveat is that the results may not be returned in the same order as the routines were issued.\n+\n+(PS: I’ve ignored handling errors in the concurrent examples. Don’t do this at home. It’s just for pure readability).\n+\n+Notice that we can still are able to use the naive approach for fetching a resource: ``naive.Get(path string)``. And that the signature of the function is exactly the same as before. That is powerful! But does it actually run faster?\n+\n+In ``main.go`` we put everything together and do measure execution time to see if its actually faster.\n+\n+<script src=\"https://gist.github.com/gronnbeck/f40f95750c5ace8337afe03b1664c275.js\"></script>\n+\n+<script src=\"https://gist.github.com/gronnbeck/6c58e8cec31565c5f70f96577b93b327.js\"></script>\n+\n+The conclusion is yes, it runs faster. Actually, it runs an order of magnitude faster. If you want to run these experiments your self or just curious about the implementation. The full example project can be found [here](https://github.com/gronnbeck/concurrencypatterns).\n+\n+## Closing notes\n+\n+We have shown the it’s easy to utilise concurrency in Go using channels and goroutines. However, this post has simplified a lot and the caveats you may encounter using channels and goroutines are not fully dressed here. So use channels and goroutines with caution. They can cause a lot of headache if over used. The general advice is to always start by building something naive before optimising.\n+\n+I hope you have enjoyed reading this post. If I’ve done something completely absurd and stupid. Please tell me so, in the comment below or on twitter ([@gronnbeck](https://twitter.com/gronnbeck)). I’m still learning and having fun with Go. And I’m eager to learn from you as well.","path":"_posts/2016-06-29-concurrency-in-go.md","position":69,"original_position":69,"commit_id":"b5baa34e1ba90aff53e5a75f30f3d29bd03294b2","original_commit_id":"b5baa34e1ba90aff53e5a75f30f3d29bd03294b2","user":{"login":"frodebjerke","id":831318,"avatar_url":"https://avatars.githubusercontent.com/u/831318?v=3","gravatar_id":"","url":"https://api.github.com/users/frodebjerke","html_url":"https://github.com/frodebjerke","followers_url":"https://api.github.com/users/frodebjerke/followers","following_url":"https://api.github.com/users/frodebjerke/following{/other_user}","gists_url":"https://api.github.com/users/frodebjerke/gists{/gist_id}","starred_url":"https://api.github.com/users/frodebjerke/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/frodebjerke/subscriptions","organizations_url":"https://api.github.com/users/frodebjerke/orgs","repos_url":"https://api.github.com/users/frodebjerke/repos","events_url":"https://api.github.com/users/frodebjerke/events{/privacy}","received_events_url":"https://api.github.com/users/frodebjerke/received_events","type":"User","site_admin":false},"body":"Think it is okay to tone down the `I’ve done something completely absurd and stupid.`","created_at":"2016-06-30T07:17:39Z","updated_at":"2016-06-30T07:17:39Z","html_url":"https://github.com/unacast/unacast.github.io/pull/24#discussion_r69082533","pull_request_url":"https://api.github.com/repos/unacast/unacast.github.io/pulls/24","_links":{"self":{"href":"https://api.github.com/repos/unacast/unacast.github.io/pulls/comments/69082533"},"html":{"href":"https://github.com/unacast/unacast.github.io/pull/24#discussion_r69082533"},"pull_request":{"href":"https://api.github.com/repos/unacast/unacast.github.io/pulls/24"}}},"pull_request":{"url":"https://api.github.com/repos/unacast/unacast.github.io/pulls/24","id":75604097,"html_url":"https://github.com/unacast/unacast.github.io/pull/24","diff_url":"https://github.com/unacast/unacast.github.io/pull/24.diff","patch_url":"https://github.com/unacast/unacast.github.io/pull/24.patch","issue_url":"https://api.github.com/repos/unacast/unacast.github.io/issues/24","number":24,"state":"open","locked":false,"title":"First draft of concurrency patterns in Go","user":{"login":"gronnbeck","id":310118,"avatar_url":"https://avatars.githubusercontent.com/u/310118?v=3","gravatar_id":"","url":"https://api.github.com/users/gronnbeck","html_url":"https://github.com/gronnbeck","followers_url":"https://api.github.com/users/gronnbeck/followers","following_url":"https://api.github.com/users/gronnbeck/following{/other_user}","gists_url":"https://api.github.com/users/gronnbeck/gists{/gist_id}","starred_url":"https://api.github.com/users/gronnbeck/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/gronnbeck/subscriptions","organizations_url":"https://api.github.com/users/gronnbeck/orgs","repos_url":"https://api.github.com/users/gronnbeck/repos","events_url":"https://api.github.com/users/gronnbeck/events{/privacy}","received_events_url":"https://api.github.com/users/gronnbeck/received_events","type":"User","site_admin":false},"body":"Feedback pl0x ","created_at":"2016-06-29T08:02:04Z","updated_at":"2016-06-30T07:17:39Z","closed_at":null,"merged_at":null,"merge_commit_sha":"7b8c66e2953101f1bf835960cc394a6d146ed185","assignee":null,"assignees":[],"milestone":null,"commits_url":"https://api.github.com/repos/unacast/unacast.github.io/pulls/24/commits","review_comments_url":"https://api.github.com/repos/unacast/unacast.github.io/pulls/24/comments","review_comment_url":"https://api.github.com/repos/unacast/unacast.github.io/pulls/comments{/number}","comments_url":"https://api.github.com/repos/unacast/unacast.github.io/issues/24/comments","statuses_url":"https://api.github.com/repos/unacast/unacast.github.io/statuses/b5baa34e1ba90aff53e5a75f30f3d29bd03294b2","head":{"label":"unacast:go-concurrency-patterns","ref":"go-concurrency-patterns","sha":"b5baa34e1ba90aff53e5a75f30f3d29bd03294b2","user":{"login":"unacast","id":10339033,"avatar_url":"https://avatars.githubusercontent.com/u/10339033?v=3","gravatar_id":"","url":"https://api.github.com/users/unacast","html_url":"https://github.com/unacast","followers_url":"https://api.github.com/users/unacast/followers","following_url":"https://api.github.com/users/unacast/following{/other_user}","gists_url":"https://api.github.com/users/unacast/gists{/gist_id}","starred_url":"https://api.github.com/users/unacast/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/unacast/subscriptions","organizations_url":"https://api.github.com/users/unacast/orgs","repos_url":"https://api.github.com/users/unacast/repos","events_url":"https://api.github.com/users/unacast/events{/privacy}","received_events_url":"https://api.github.com/users/unacast/received_events","type":"Organization","site_admin":false},"repo":{"id":44965832,"name":"unacast.github.io","full_name":"unacast/unacast.github.io","owner":{"login":"unacast","id":10339033,"avatar_url":"https://avatars.githubusercontent.com/u/10339033?v=3","gravatar_id":"","url":"https://api.github.com/users/unacast","html_url":"https://github.com/unacast","followers_url":"https://api.github.com/users/unacast/followers","following_url":"https://api.github.com/users/unacast/following{/other_user}","gists_url":"https://api.github.com/users/unacast/gists{/gist_id}","starred_url":"https://api.github.com/users/unacast/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/unacast/subscriptions","organizations_url":"https://api.github.com/users/unacast/orgs","repos_url":"https://api.github.com/users/unacast/repos","events_url":"https://api.github.com/users/unacast/events{/privacy}","received_events_url":"https://api.github.com/users/unacast/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/unacast/unacast.github.io","description":"Unacast Tech Blog","fork":false,"url":"https://api.github.com/repos/unacast/unacast.github.io","forks_url":"https://api.github.com/repos/unacast/unacast.github.io/forks","keys_url":"https://api.github.com/repos/unacast/unacast.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/unacast/unacast.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/unacast/unacast.github.io/teams","hooks_url":"https://api.github.com/repos/unacast/unacast.github.io/hooks","issue_events_url":"https://api.github.com/repos/unacast/unacast.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/unacast/unacast.github.io/events","assignees_url":"https://api.github.com/repos/unacast/unacast.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/unacast/unacast.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/unacast/unacast.github.io/tags","blobs_url":"https://api.github.com/repos/unacast/unacast.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/unacast/unacast.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/unacast/unacast.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/unacast/unacast.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/unacast/unacast.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/unacast/unacast.github.io/languages","stargazers_url":"https://api.github.com/repos/unacast/unacast.github.io/stargazers","contributors_url":"https://api.github.com/repos/unacast/unacast.github.io/contributors","subscribers_url":"https://api.github.com/repos/unacast/unacast.github.io/subscribers","subscription_url":"https://api.github.com/repos/unacast/unacast.github.io/subscription","commits_url":"https://api.github.com/repos/unacast/unacast.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/unacast/unacast.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/unacast/unacast.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/unacast/unacast.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/unacast/unacast.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/unacast/unacast.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/unacast/unacast.github.io/merges","archive_url":"https://api.github.com/repos/unacast/unacast.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/unacast/unacast.github.io/downloads","issues_url":"https://api.github.com/repos/unacast/unacast.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/unacast/unacast.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/unacast/unacast.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/unacast/unacast.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/unacast/unacast.github.io/labels{/name}","releases_url":"https://api.github.com/repos/unacast/unacast.github.io/releases{/id}","deployments_url":"https://api.github.com/repos/unacast/unacast.github.io/deployments","created_at":"2015-10-26T12:05:04Z","updated_at":"2016-03-14T09:06:31Z","pushed_at":"2016-06-29T08:02:04Z","git_url":"git://github.com/unacast/unacast.github.io.git","ssh_url":"git@github.com:unacast/unacast.github.io.git","clone_url":"https://github.com/unacast/unacast.github.io.git","svn_url":"https://github.com/unacast/unacast.github.io","homepage":null,"size":39979,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":2,"forks":0,"open_issues":2,"watchers":0,"default_branch":"master"}},"base":{"label":"unacast:master","ref":"master","sha":"326bb269bcda121314af5319bddf717e1784e443","user":{"login":"unacast","id":10339033,"avatar_url":"https://avatars.githubusercontent.com/u/10339033?v=3","gravatar_id":"","url":"https://api.github.com/users/unacast","html_url":"https://github.com/unacast","followers_url":"https://api.github.com/users/unacast/followers","following_url":"https://api.github.com/users/unacast/following{/other_user}","gists_url":"https://api.github.com/users/unacast/gists{/gist_id}","starred_url":"https://api.github.com/users/unacast/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/unacast/subscriptions","organizations_url":"https://api.github.com/users/unacast/orgs","repos_url":"https://api.github.com/users/unacast/repos","events_url":"https://api.github.com/users/unacast/events{/privacy}","received_events_url":"https://api.github.com/users/unacast/received_events","type":"Organization","site_admin":false},"repo":{"id":44965832,"name":"unacast.github.io","full_name":"unacast/unacast.github.io","owner":{"login":"unacast","id":10339033,"avatar_url":"https://avatars.githubusercontent.com/u/10339033?v=3","gravatar_id":"","url":"https://api.github.com/users/unacast","html_url":"https://github.com/unacast","followers_url":"https://api.github.com/users/unacast/followers","following_url":"https://api.github.com/users/unacast/following{/other_user}","gists_url":"https://api.github.com/users/unacast/gists{/gist_id}","starred_url":"https://api.github.com/users/unacast/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/unacast/subscriptions","organizations_url":"https://api.github.com/users/unacast/orgs","repos_url":"https://api.github.com/users/unacast/repos","events_url":"https://api.github.com/users/unacast/events{/privacy}","received_events_url":"https://api.github.com/users/unacast/received_events","type":"Organization","site_admin":false},"private":false,"html_url":"https://github.com/unacast/unacast.github.io","description":"Unacast Tech Blog","fork":false,"url":"https://api.github.com/repos/unacast/unacast.github.io","forks_url":"https://api.github.com/repos/unacast/unacast.github.io/forks","keys_url":"https://api.github.com/repos/unacast/unacast.github.io/keys{/key_id}","collaborators_url":"https://api.github.com/repos/unacast/unacast.github.io/collaborators{/collaborator}","teams_url":"https://api.github.com/repos/unacast/unacast.github.io/teams","hooks_url":"https://api.github.com/repos/unacast/unacast.github.io/hooks","issue_events_url":"https://api.github.com/repos/unacast/unacast.github.io/issues/events{/number}","events_url":"https://api.github.com/repos/unacast/unacast.github.io/events","assignees_url":"https://api.github.com/repos/unacast/unacast.github.io/assignees{/user}","branches_url":"https://api.github.com/repos/unacast/unacast.github.io/branches{/branch}","tags_url":"https://api.github.com/repos/unacast/unacast.github.io/tags","blobs_url":"https://api.github.com/repos/unacast/unacast.github.io/git/blobs{/sha}","git_tags_url":"https://api.github.com/repos/unacast/unacast.github.io/git/tags{/sha}","git_refs_url":"https://api.github.com/repos/unacast/unacast.github.io/git/refs{/sha}","trees_url":"https://api.github.com/repos/unacast/unacast.github.io/git/trees{/sha}","statuses_url":"https://api.github.com/repos/unacast/unacast.github.io/statuses/{sha}","languages_url":"https://api.github.com/repos/unacast/unacast.github.io/languages","stargazers_url":"https://api.github.com/repos/unacast/unacast.github.io/stargazers","contributors_url":"https://api.github.com/repos/unacast/unacast.github.io/contributors","subscribers_url":"https://api.github.com/repos/unacast/unacast.github.io/subscribers","subscription_url":"https://api.github.com/repos/unacast/unacast.github.io/subscription","commits_url":"https://api.github.com/repos/unacast/unacast.github.io/commits{/sha}","git_commits_url":"https://api.github.com/repos/unacast/unacast.github.io/git/commits{/sha}","comments_url":"https://api.github.com/repos/unacast/unacast.github.io/comments{/number}","issue_comment_url":"https://api.github.com/repos/unacast/unacast.github.io/issues/comments{/number}","contents_url":"https://api.github.com/repos/unacast/unacast.github.io/contents/{+path}","compare_url":"https://api.github.com/repos/unacast/unacast.github.io/compare/{base}...{head}","merges_url":"https://api.github.com/repos/unacast/unacast.github.io/merges","archive_url":"https://api.github.com/repos/unacast/unacast.github.io/{archive_format}{/ref}","downloads_url":"https://api.github.com/repos/unacast/unacast.github.io/downloads","issues_url":"https://api.github.com/repos/unacast/unacast.github.io/issues{/number}","pulls_url":"https://api.github.com/repos/unacast/unacast.github.io/pulls{/number}","milestones_url":"https://api.github.com/repos/unacast/unacast.github.io/milestones{/number}","notifications_url":"https://api.github.com/repos/unacast/unacast.github.io/notifications{?since,all,participating}","labels_url":"https://api.github.com/repos/unacast/unacast.github.io/labels{/name}","releases_url":"https://api.github.com/repos/unacast/unacast.github.io/releases{/id}","deployments_url":"https://api.github.com/repos/unacast/unacast.github.io/deployments","created_at":"2015-10-26T12:05:04Z","updated_at":"2016-03-14T09:06:31Z","pushed_at":"2016-06-29T08:02:04Z","git_url":"git://github.com/unacast/unacast.github.io.git","ssh_url":"git@github.com:unacast/unacast.github.io.git","clone_url":"https://github.com/unacast/unacast.github.io.git","svn_url":"https://github.com/unacast/unacast.github.io","homepage":null,"size":39979,"stargazers_count":0,"watchers_count":0,"language":"JavaScript","has_issues":true,"has_downloads":true,"has_wiki":true,"has_pages":true,"forks_count":0,"mirror_url":null,"open_issues_count":2,"forks":0,"open_issues":2,"watchers":0,"default_branch":"master"}},"_links":{"self":{"href":"https://api.github.com/repos/unacast/unacast.github.io/pulls/24"},"html":{"href":"https://github.com/unacast/unacast.github.io/pull/24"},"issue":{"href":"https://api.github.com/repos/unacast/unacast.github.io/issues/24"},"comments":{"href":"https://api.github.com/repos/unacast/unacast.github.io/issues/24/comments"},"review_comments":{"href":"https://api.github.com/repos/unacast/unacast.github.io/pulls/24/comments"},"review_comment":{"href":"https://api.github.com/repos/unacast/unacast.github.io/pulls/comments{/number}"},"commits":{"href":"https://api.github.com/repos/unacast/unacast.github.io/pulls/24/commits"},"statuses":{"href":"https://api.github.com/repos/unacast/unacast.github.io/statuses/b5baa34e1ba90aff53e5a75f30f3d29bd03294b2"}}}}
{"actor":{"display_login":"frodebjerke"}}