In the world around us many un/planned activities/events simultaneously and often independent of each other. While reading this, you may receive a phone call and while still on the call you might receive a few push notifications on your mobile from Facebook or WhatsApp. While reading this and attending to that phone call, you might be listening to your favorite number played in your home theater and you may reach out to adjust the volume level. Does this make sense or you're still scratching your head... yet another activity!
Yes, the world is indeed parallel. Apart from everything else even in technology world also the concept of parallelism can be observed viz. Multicore machines, networks, clouds of CPUs, Users request to a web server - all are parallel.
That's probably the reason we get stuck at times when we try to solve a real world problem using the non parallel approach (read Object Oriented techniques).
Almost a decade back the following was one of the pet interview questions thrown mostly to any developer with 3+ years of experience:
- Have you implemented multi-threading in x language?
Where x = C#, Java or any other language that supports multi-threading.
Without any doubt, I agree (like most of us) that multi-threading is complex and a beast too big to be tamed by mere mortals with average intelligence. You can agree to disagree with me and may call this a common fallacy :-)
- Goroutines has made concurrency / multi-threading easier to use.
If you already understand the basics move on to Go Channels
What is Concurrency?
Concurrent = happening, or done at the same time
Concurrency is a way to structure a program by breaking it into pieces that can be executed independently.
The Go tools of concurrency make it almost trivial to build a safe, working, scalable & parallel design.
Here, Processes in the general sense, not Windows/Linux processes.
Is Concurrency same as Parallelism?
No. They are not same, but are related.
Rob Pike says:
- Concurrency is about dealing with lots of things at once.
- Parallelism is about doing lots of things at once.
- Concurrency is about structure, parallelism is about execution.
- Concurrency provides a way to structure a solution to solve a problem that may (but not necessarily) be parallelizable.
Here's an excellent video by none other than one of the creators of Go - Rob Pike Concurrency Is Not Parallelism
Go tools to Implement Concurrency
- Goroutines - concurrent execution.
- Channels - synchronization and messaging.
- Select - multi-way concurrent control.
What is Goroutine?
A goroutine is a function running independently in the same address space as other goroutines. They're a bit like threads, but they're much cheaper.
A goroutine is a lightweight thread of execution. It's routine to create thousands of goroutines in one program. Stacks start small, but grow and shrink as required.
Goroutines aren't free, but they're very cheap.
Important Update: To synchronize concurrently running functions Go provides Channels.
The following code sample uses time.Sleep() to sync the goroutines - a bad programming practice and should be avoided. Synchronizing your goroutines like this can introduce several serious bugs in your code. The purpose of this code sample is to give you a feel of goroutines in action.
Important Update: To synchronize concurrently running functions Go provides Channels.
The following code sample uses time.Sleep() to sync the goroutines - a bad programming practice and should be avoided. Synchronizing your goroutines like this can introduce several serious bugs in your code. The purpose of this code sample is to give you a feel of goroutines in action.
package main import ( "fmt" "time" ) func Tortoise() { time.Sleep(5 * 1e9) fmt.Println("Tortoise is still somewhere near the Starting Line.") time.Sleep(10 * 1e9) fmt.Println("Tortoise reaches 10 miles after 10 sec.") time.Sleep(10 * 1e9) fmt.Println("Tortoise reaches 20 miles after 20 sec.") time.Sleep(6 * 1e9) fmt.Println("Surprise... Tortoise reaches finish line! Completes 26 miles in 26 sec") } func Rabbit() { time.Sleep(2 * 1e9) fmt.Println("Rabbit reaches 10 miles after 2 sec.") time.Sleep(4 * 1e9) fmt.Println("Rabbit reaches 20 miles after 4 sec.") time.Sleep(7 * 1e9) fmt.Println("Rabbit takes power nap.") time.Sleep(20 * 1e9) fmt.Println("Rabbit reaches the finishing line! OOOPs Rabbit lost the race!") } func main() { fmt.Println("Marathon Race - 26 miles, Tortoise vs. Rabbit.") fmt.Println("On your mark. Starting pistol fired...") fmt.Println("The Rabbit sprints ahead while the Tortoise is dead slow.") go Rabbit() go Tortoise() time.Sleep(34 * 1e9) fmt.Println("Keep GO-ing :-) even if you're slow.") }
Output
Marathon Race - 26 miles, Tortoise vs. Rabbit.
On your mark. Starting pistol fired...
The Rabbit sprints ahead while the Tortoise is dead slow.
Rabbit reaches 10 miles after 2 sec.
Tortoise is still somewhere near the Starting Line.
Rabbit reaches 20 miles after 4 sec.
Rabbit takes power nap.
Tortoise reaches 10 miles after 10 sec.
Tortoise reaches 20 miles after 20 sec.
Surprise... Tortoise reaches finish line! Completes 26 miles in 26 sec
Rabbit reaches the finishing line! OOOPs Rabbit lost the race!
Keep GO-ing :-) even if you're slow
A Brief Description of Above Code
time.Sleep(5 * 1e9)
In above code we're introducing a time delay of 5 seconds. Replace 5 with 10 to get a delay of 10 seconds. Know more about Sleep function here.
Apart from the main() function there are other 2 functions namely Tortoise() and Rabbit().
In the main() function we've 2 goroutines namely go Rabbit() and go Tortoise().
From the output you can easily see how both the functions are getting executed concurrently.
Important: By default, Go code is NOT parallel i.e. only a single core or processor is dedicated to a Go-program, regardless of how many goroutines are started in it; so these goroutines are running concurrent, they are not running in parallel: only one goroutine is running at a time.
How can I improve this code? Please share your thoughts.
A big no-no, basing concurrent synchronization based on time. I realize this is how we humans think of this but ultimately this leads to huge mistakes like queue starvation, miss-dispatch, deadlocks,(at least in C) dangling pointers and more... these can be nightmares to debug.
ReplyDeleteEven in your simple example, a proper synchronization method should be used, in go I figure you should use channels... For the rest of us, we are used to things like SysV IPC.
Yes I understand that and fully agree with you. My sole intention was to get a feel of how goroutines work. Anyways, Thanks for the info. I've updated the article with a red marked note.
Delete