Goroutines - Concurrency in Goprogramming




Welcome everyone to part 18 of the Go Language programming tutorial series, where we'll be talking about concurrency in Go. First, let's acknowledge what concurrency is, and what it isn't. Concurrency is dealing with multiple things at once, and it is not actually doing multiple things simultaneously (parallelism).

Let's consider an example of a CEO who is working on writing a company memo, but is also answering emails as they come in. This CEO is working concurrently. They're writing the memo, and then, when a bell goes off that they got an email, they finish the sentence they were working on in the memo, pause their work on the memo and shift over to complete the email task. Then they bop back over and pick up where they left off on the memo. They're concurrently doing emails and the memo, but they're not doing emails and the memo simultaneously in parallel.

Great, let's try this in Go! A Go Routine is a lightweight thread, which we can easily spawn by simply prefacing the function call with go. For now, let's just use a basic hello world example:

package main

import (
	"fmt"
	"time"
)

func say(s string) {
	for i:=0; i < 3; i++ {
		time.Sleep(100*time.Millisecond)
		fmt.Println(s)
	}	
}

func main() {
	go say("Hey")
	say("there")
}

In the above case, the say("hey") is being run as a goroutine, but say("there") is just a regular function call. If we run this, output is likely:

there
Hey
there
Hey
there

One thing to note about these goroutines is that they aren't going to necessarily run or complete before your program ends. For example, what if we changed our main function to:

func main() {
	say("Hey")
	go say("there")
}

In this case, the say("Hey") runs first, but it's not a goroutine, so it's going to run in full first, hogging resources for itself first. Unfortunately, after this, the only other line of code is for the goroutine to run, which doesn't really have any priority to actually keep the program overall running, so the program is done. What might happen if we did:

func main() {
	go say("Hey")
	go say("there")
}

... Nothing at all!

But, if we just add a sleep at the end:

func main() {
	go say("Hey")
	go say("there")
	time.Sleep(1000*time.Millisecond)
}

Then we get something like:

Hey
there
Hey
there
Hey
there

Very cool, but this only works if we know the exact time that our goroutines will take, or something close. If we're too high in our estimation, we waste time, if we're too low, our processes don't run! Neither of these are good. There are some hacky ways we could solve this, but, chances are, the creators of Go probably have thought of this! ...and they have. We'll be talking in the next tutorial about how we can make certain our processes are complete before continuing along.

The next tutorial:





  • Introduction to the Go Programming Language
  • Go Language Syntax
  • Go Language Types
  • Pointers in Go Programming
  • Simple Web App in Go Programming
  • Structs in the Go Programming Language
  • Methods in Go Programming
  • Pointer Receivers in Go Programming
  • More Web Dev in Go Language
  • Acessing the Internet in Go
  • Parsing XML with Go Programming
  • Looping in Go Programming
  • Continuing our Go Web application
  • Mapping in Golang
  • Mapping Golang sitemap data
  • Golang Web App HTML Templating
  • Applying templating to our Golang web app
  • Goroutines - Concurrency in Goprogramming
  • Synchronizing Goroutines - Concurrency in Golang
  • Defer - Golang
  • Panic and Recover in Go Programming
  • Go Channels - Concurrency in Go
  • Go Channels buffering, iteration, and synchronization
  • Adding Concurrency to speed up our Golang Web Application