In modern programming, managing how multiple processes and tasks are handled is an important aspect of performance improvements. Two fundamental concepts often discussed in this context are concurrency and parallelism. While these terms are sometimes used interchangeably, they have distinct meanings and implications, especially when working with the Go programming language. In this article, we will delve into the differences between concurrency and parallelism, providing code examples to illustrate these concepts in Go.
Table of Contents
Understanding Concurrency
Concurrency is about dealing with lots of things at once. In Go, concurrency is supported natively thanks to goroutines and channels. A goroutine is a lightweight thread of execution, making concurrent programming straightforward.
Here is a simple example of concurrency in Go using goroutines:
package main
import (
"fmt"
"time"
)
func sayHello() {
for i := 0; i < 5; i++ {
fmt.Println("Hello")
time.Sleep(100 * time.Millisecond)
}
}
func sayWorld() {
for i := 0; i < 5; i++ {
fmt.Println("World")
time.Sleep(100 * time.Millisecond)
}
}
func main() {
go sayHello()
go sayWorld()
time.Sleep(1 * time.Second)
}
In this example, both sayHello() and sayWorld() are launched as goroutines and run concurrently. Notice how you use the go keyword to call the functions. Even though these functions are executed in order, they will interleave their output, showing concurrency.
Understanding Parallelism
Parallelism, on the other hand, is about doing multiple things at the same time. For this to occur, a multicore processor is required to handle threads actually running simultaneously. Parallelism can be tricky to achieve because it often requires careful management of shared resources.
Let’s modify our previous example to achieve parallelism:
package main
import (
"fmt"
"runtime"
"time"
)
func sayHello() {
for i := 0; i < 5; i++ {
fmt.Println("Hello")
time.Sleep(100 * time.Millisecond)
}
}
func sayWorld() {
for i := 0; i < 5; i++ {
fmt.Println("World")
time.Sleep(100 * time.Millisecond)
}
}
func main() {
// Set the number of OS threads to use
runtime.GOMAXPROCS(2)
go sayHello()
go sayWorld()
time.Sleep(1 * time.Second)
}
In this example, runtime.GOMAXPROCS(2) sets the number of operating system threads that our program can use, enabling the Go runtime to run the goroutines on two different CPU cores concurrently, achieving parallelism.
Conclusion
Understanding the difference between concurrency and parallelism will help you leverage the full potential of Go when optimizing your applications' performance. By using goroutines effectively and configuring them for multicore execution, you can dramatically improve your program’s capability to handle multiple tasks and processes efficiently.