Development & AI | Alper Akgun
Go lang's shining feature is built-in support for concurrency using goroutines and channels. Goroutines are lightweight threads well-suited for scalable and efficient applications.
You use the "go" keyword to run a function in a parallel thread; as a goroutine.
package main
import (
"fmt"
"time"
)
func f1(from string) {
for i := 0; i < 3; i++ {
fmt.Println(from, ":", i)
}
}
func main() {
f1("direct call")
go f1("goroutine call")
go func(from string) {
fmt.Println(from)
}("anonymous call")
time.Sleep(time.Second)
fmt.Println("done!")
}
You use channels to send messages across goroutines. You can set a buffer length for channels.
package main
import (
"fmt"
"time"
)
func worker(done chan bool) {
fmt.Print("working...")
time.Sleep(time.Second)
fmt.Println("done")
done <- true
}
func ping(pings chan<- string, msg string) {
pings <- msg
}
// this channel uses pings for receives, and pongs for sends.
func pong(pings <-chan string, pongs chan<- string) {
msg := <-pings
pongs <- msg
}
func main() {
pings := make(chan string)
go func() { pings <- "ping!" }()
msg := <-messages
fmt.Println(msg)
// create a channel which can buffer upto 2 values.
messages := make(chan string, 2)
messages <- "buffered"
messages <- "channel"
fmt.Println(<-messages)
fmt.Println(<-messages)
// channel sync
done := make(chan bool, 1)
go worker(done)
// waits until the goroutine notifies as done
<-done
// mark channels as receive-only or send-only
pings := make(chan string, 1)
pongs := make(chan string, 1)
ping(pings, "passed message")
pong(pings, pongs)
fmt.Println(<-pongs)
// Non-blocking channel operations work with a "select" with default argument
messages := make(chan string)
signals := make(chan bool)
select {
case msg := <-messages:
fmt.Println("received message", msg)
default:
fmt.Println("no message received")
}
msg := "hi"
select {
case messages <- msg:
fmt.Println("sent message", msg)
default:
fmt.Println("no message sent")
}
select {
case msg := <-messages:
fmt.Println("received message", msg)
case sig := <-signals:
fmt.Println("received signal", sig)
default:
fmt.Println("no activity")
}
// Closing a channel
jobs := make(chan int, 5)
finished := make(chan bool)
go func() {
for {
j, more := <-jobs
if more {
fmt.Println("received job", j)
} else {
fmt.Println("received all jobs")
finished <- true
return
}
}
}()
for j := 1; j <= 3; j++ {
jobs <- j
fmt.Println("sent job", j)
}
close(jobs)
fmt.Println("sent all jobs")
<-finished
// range over a closed channel
queue := make(chan string, 2)
queue <- "one"
queue <- "two"
close(queue)
for elem := range queue {
fmt.Println(elem)
}
}
With Golang's "select" you can wait for multiple channel operations.
package main
import (
"fmt"
"time"
)
func main() {
c1 := make(chan string)
c2 := make(chan string)
c3 := make(chan string)
go func() {
time.Sleep(1 * time.Second)
c1 <- "one"
}()
go func() {
time.Sleep(2 * time.Second)
c2 <- "two"
}()
go func() {
time.Sleep(3 * time.Second)
c3 <- "three"
}()
for i := 0; i < 2; i++ {
select {
case msg1 := <-c1:
fmt.Println("received: ", msg1)
case msg2 := <-c2:
fmt.Println("received: ", msg2)
case msg3 := <-c3:
fmt.Println("received: ", msg3)
}
}
// The following function will timeout.
c1 := make(chan string, 1)
go func() {
time.Sleep(2 * time.Second)
c1 <- "result 1"
}()
select {
case res := <-c1:
fmt.Println(res)
case <-time.After(1 * time.Second):
fmt.Println("timeout 1")
}
}