Read
Concurrency Parallelism and Goroutines for Beginners before you proceed further.
CodeRef# 1
package main
import "fmt"
func main() {
fmt.Println("A message from main function")
go fmt.Println("A message from goroutine")
}
What do you think is the Output of above code?
A message from main function
Play with the Above Code
In most of the cases you would get the above output. But there can be cases where you may get both the messages as output. Here we can conclude that the output of the above code is undefined.
Why didn't it always print the second message from the goroutine?
The func main() here is running in a single OS execution thread. Once the main() code has finished writing "A message from main function" it doesn't wait for the other subroutine (called using the keyword go) to finish its task.
A quick fix solution that can fix this issue is to introduce a delay in the main(). Something like:
CodeRef# 2
package main
import "fmt"
import "time"
func main() {
fmt.Println("A message from main function")
go fmt.Println("A message from goroutine")
time.Sleep(1 * time.Second)
}
Output CodeRef# 2
A message from main function
A message from goroutine
It seems we've achieved what we expected out of that code snippet. Right?
Though we've got the expected result it is a good example of bad programming. Time based synchronization must be avoided as they are inefficient and might lead to ugly bugs. You never know if the process will take 1 second (as introduced in the above example) or more or less than 1 second. Also, the time of execution may differ on different machine configuration. Avoid it!
CodeRef# 3
package main
import "fmt"
import "runtime"
func main() {
fmt.Println("A message from main function")
go fmt.Println("A message from goroutine")
runtime.Gosched()
}
Output CodeRef# 3
A message from main function
A message from goroutine
Play with CodeRef# 3
This looks like a better solution. Here Gosched() has switched the execution context so that the other goroutine can finish its task. Gosched() yields the processor, allowing other goroutines to run.
What are Channels?
Goroutines run concurrently as independent units and hence there must be some mechanism to synchronize the access to shared memory. This is essential to prevent deadlock like scenarios. Go has Channels to sync goroutines.
The Little Go Book by Karl Seguin describes channels as:
You can think Channel as a communication pipe between goroutines which is used to pass data. In other words, A goroutine that has data can pass it to another goroutine via a channel. The result is that, at any point in time, only one goroutine has access to the data.
Remember, a channel can only transmit data-items of one datatype.
Type of Channels
- Unbuffered or Synchronous
- Buffered or Asynchronous
Declaring & Allocating Memory to Channels
intC := make(chan int) //default capacity = 0
strC := make(chan string, 3) // non-zero capacity
Channel Supports 2 operations SEND and RECEIVE
Channel <- Data // SEND 'data' to a channel
msg :- <- Channel //RECEIVE a message & Assign it to a variable msg
r := make(<-chan bool) // can only read from
w := make(chan<- []os.FileInfo) // can only write to
Note: The left arrow operator <- is used for both sending & receiving data. The arrow head of the operator points in the direction of data flow. The channel SEND & RECEIVE operations are Atomic i.e. they always complete without any interruption.
CodeRef# 4
package main
import (
"fmt"
"time"
)
func main() {
ch := make(chan int)
go iSend(ch)
go iReceive(ch)
time.Sleep(time.Second * 1)
}
func iSend(ch chan int) {
ch <- 1
ch <- 3
ch <- 5
ch <- 7
}
func iReceive(ch chan int) {
var info int
// infinite for loop, executes till 'ch' is empty
for {
info = <-ch
fmt.Printf("%d ", info)
}
}
Output CodeRef# 4
1 3 5 7
Play with the Code
CodeRef# 5
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
| package main
import "fmt"
import "time"
func main() {
ch1 := make(chan int)
go send(ch1)
go receive(ch1)
time.Sleep(time.Second * 1)
}
func send(ch chan int) {
for i := 1; i < 7; i++ {
ch <- i
}
}
func receive(ch chan int) {
for {
fmt.Println(<-ch)
}
}
|
Output CodeRef# 5
1
2
3
4
5
6
Play the code here
CodeRef# 6
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
| package main
import "fmt"
import "time"
func main() {
ch := make(chan int)
go sendOdd(ch)
go sendEven(ch)
go receive(ch)
time.Sleep(time.Second * 1)
}
//This func to generate only Odd numbers
func sendOdd(ch chan int) {
for i := 1; i < 9; i++ {
if i%2 != 0 {
ch <- i
}
}
}
//This func to generate only Even numbers
func sendEven(ch chan int) {
for i := 1; i < 9; i++ {
if i%2 == 0 {
ch <- i
}
}
}
//Recive & Print numbers
func receive(ch chan int) {
for {
v := <-ch
fmt.Println(v)
}
}
|
Play with the Code
Output CodeRef# 6
1
2
4
6
8
3
5
7
The above code is self explanatory. Play around and you'll soon get the knack of it. Please share your views to improve this article.
References
http://guzalexander.com/2013/12/06/golang-channels-tutorial.html