While trying new programming language, you should learn about the basic concept that make that particular programming language worth your time and effort. In this case, Iam trying golang that offer us goroutines. So, why goroutines ?
In this story, I wan’t to share what I have learned about the basic of 3 out of many things that make go worth to try. These three stacks is what you should know how concurrent concept work in go. To start reading this story, you better read about parallelism and concurrency in programming.
Quoting tour golang, “A goroutine is a lightweight thread managed by the Go runtime”. It can achieve what thread(In java) could achieve using less resources.
Keep in mind:
- When calling goroutines, the control immediately move to the next line. The control does not wait for goroutines to finish.
- The main goroutines should be running to run any other goroutine. Terminating the main goroutines will terminate all of goroutines that currently running.
Advantages of Goroutines over Thread
- Lightweight, only few kb in size that can grow and sink while threads need to be specified and fixed
- Gorutines are N:1 to OS thread while thread is 1:1 to OS thread
- Goroutines communicate using channel(will explain later)
All you need to do is to write keyword “go” and you will have new goroutines running.
Run the program.
What do you get ?
Remember “the control immediately move to the next line after calling goroutines” ?, means after executing line 7(calling goroutines), the control immediately moved to line 8(printing “Hello After”) without waiting for function HelloGo finishing its execution, that is why the program didn’t print “Hello Goroutine”
How to see the result of goroutines?
Let’s modify the program
The result ?
In line 11 the main goroutine tell the program to sleep/wait for a second, therefore goroutine HelloGo has enough time to finish its execution and printed “Hello Goroutine”.
This is one way to synchronized goroutines, but the cons is, we don’t know exactly how much time the goroutine needed to finish its execution. This is ugly, but it can help us to understand how goroutines work. The correct way is using channels.
What is channels ? Grab your favourite translator, and translate channels to your own language. In bahasa, channels means saluran. Following the words and the language context(bahasa) then channels is commonly used to transfer water from one end to another end. Thats it. Now imagine a water is a data and the end of each channels is goroutines. That’s right channels is what we used to transfer data between goroutines, other than that, we can utilize channel to synchronized gourotines.
Lets modify the code
Lets examine the code
Line 8 print “Hello Before”
Line 10 declaring and instance a channel called c with boolean type
Line 11 run goroutine HelloGo and passing channel c as parameter
Line 12 receive/read from channel c, you can print this result to evaluate the varable
Line 14 print “Hello After”
Line 18 print “Hello Goroutine” in goroutine
Line 19 send/writes value(true) to channel c
To understand how this program works, we need to understand the nature of a go channels by sending and receiving value.
Sending and Receiving value from channels.
Goroutines are blocked by default by sending and receiving value from a channel. What does it mean ? When data are sent to channel, the control is blocked in the send statement until other goroutine is received from that channel, and it goes both ways, which means when data are received from a channel, the control is blocked until other goroutines write to that channel.
Let’s back to the code.
In line 12, we are receiving data from channel c, then the control is blocked here until other goroutines write some data to channels. This makes our program currently stop executing. Before line 12 executed, in line 11 we run goroutines HelloGo, in this go routines, we print “Hello GoRoutine”. Until this state, our main gouroutines is still blocked, resulting the program printing “Hello Goroutine”. In line 19, we writes data to channels c which make our main goroutines stop blocking and countinue to execute the code resulting our main goroutines printing “Hello After”.
That is the most basic implementation of channels in go.
Simple case to utilize the use of goroutines and channels
The program above shows how we create two goroutines to sum array’s items using one function.
Deadlock is an important factor to know since it may cause your program to panic when dealing with channels. Deadlock may happen when you write data to a channel without make a goroutines to receive the data from that channels. And it goes both ways, when you read the data from a channel, then it expects for other goroutines to write to the same channel. In case you don’t follow this rules, then your program will panic in a runtime with deadlock.
Let’s modify the program by removing line 19(writing to channels c)
fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan receive]:
exit status 2
Runtime panic happen with deadlock because in line 12, we read from channels c but we don’t writes to that channel while the runtime expected others goroutines to write to that channels. It makes the program panic with deadlock at line 12. That’s why the program still prints “Hello Before” and “Hello Goroutine” but failed to print “Hello After” because the control doesn’t reach the code after line 12.
Pipeline is a concept in how to utilize the use of goroutines and channels. In short, pipeline is a series of stages that connected by channels. Each stage is a goroutines that:
- Receive value/data from channels
- Performing logic/computation on that value resulting new data
- Send values via channels
Program below is the most simple implementation of pipeline
What does the program do ?
The program’s goal is to print a string from a set of words. Following the concept of pipeline, then we do three stages of goroutine.
Line 6, we call a function called gen that accepts series of string, then writes them all, closes the channel, and returns the channels.
Line 7, we call function concate with channels c as parameter to do logic which is concate all string in the channel.
Line 30, we write logic result to the channel, and return the channel, then we print the result in line 9.
That’s all about the most basic concept we must know about Goroutines, Channels and Pipeline.
Look at this code
fatal error: all goroutines are asleep - deadlock!goroutine 1 [chan send]:
exit status 2
Because it violates channels rules, we write to channels c, but we didn’t read from that channels.
Now lets modify the code
The result ?
No error. Why ?
Share with us what do you think and how should we handle/catch the error on a case like this.