Swiftorial Logo
Home
Swift Lessons
Matchups
CodeSnaps
Tutorials
Career
Resources

Goroutines in Go Programming

Introduction to Goroutines

Goroutines are functions or methods that run concurrently with other functions or methods. They are a lightweight thread managed by the Go runtime.

Goroutines are a fundamental part of Go's concurrency model and enable the handling of many tasks simultaneously. They are much cheaper than threads and can be created in large numbers without overwhelming system resources.

Creating a Goroutine

To create a goroutine, you use the go keyword followed by a function call. The function will then execute concurrently with the calling function.

package main

import (
    "fmt"
    "time"
)

func sayHello() {
    fmt.Println("Hello, World!")
}

func main() {
    go sayHello()
    time.Sleep(1 * time.Second)
}

Output:

Hello, World!

In this example, the sayHello function runs as a goroutine. The main function sleeps for one second to give the goroutine time to complete.

Anonymous Functions as Goroutines

Anonymous functions can also be used as goroutines. This can be useful for quick, inline concurrent tasks.

package main

import (
    "fmt"
    "time"
)

func main() {
    go func() {
        fmt.Println("Hello from anonymous function!")
    }()
    time.Sleep(1 * time.Second)
}

Output:

Hello from anonymous function!

In this example, an anonymous function is created and executed as a goroutine.

Synchronization with WaitGroups

Go provides a sync.WaitGroup type to wait for a collection of goroutines to finish executing. This is useful for synchronizing goroutines.

package main

import (
    "fmt"
    "sync"
)

func printMessage(message string, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Println(message)
}

func main() {
    var wg sync.WaitGroup
    messages := []string{"Hello", "World", "from", "goroutines"}

    for _, message := range messages {
        wg.Add(1)
        go printMessage(message, &wg)
    }

    wg.Wait()
}

Output:

Hello
World
from
goroutines

In this example, we use a sync.WaitGroup to wait for all goroutines to finish before the program exits.

Handling Goroutine Lifecycle with Channels

Channels are another powerful feature in Go that can be used to communicate between goroutines and synchronize their execution. Channels provide a way for one goroutine to send data to another goroutine.

package main

import (
    "fmt"
)

func printNumbers(ch chan int) {
    for i := 1; i <= 5; i++ {
        ch <- i
    }
    close(ch)
}

func main() {
    ch := make(chan int)

    go printNumbers(ch)

    for num := range ch {
        fmt.Println(num)
    }
}

Output:

1
2
3
4
5

In this example, a channel ch is used to send integers from the printNumbers goroutine to the main goroutine.

Buffered Channels

Channels can also be buffered. A buffered channel has a capacity that defines the number of elements it can hold before it blocks.

package main

import (
    "fmt"
)

func main() {
    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3

    fmt.Println(<-ch)
    fmt.Println(<-ch)
    fmt.Println(<-ch)
}

Output:

1
2
3

In this example, the channel ch is buffered with a capacity of 3. This allows three integers to be sent to the channel without blocking.

Conclusion

Goroutines are a powerful feature in Go that enable concurrent execution of functions. They are lightweight and easy to create, making them ideal for tasks that require concurrency. By using tools like sync.WaitGroup and channels, you can effectively manage and synchronize goroutines in your Go programs.

Understanding and utilizing goroutines will help you write more efficient and responsive applications in Go.