Detailed explanation of the synchronization and mutual exclusion mechanism of Goroutines in Golang concurrent programming
With the popularity of multi-core processors and the continuous improvement of computer performance, how to make full use of multiple processor cores for parallel computing has become a concern for developers an important issue faced. Concurrent programming is one of the key technologies to solve this problem. In Golang, Goroutines and Channels are widely used to implement concurrent programming. Among them, Goroutines are lightweight threads that can achieve high-concurrency task processing. In order to ensure the correct cooperation between multiple Goroutines, synchronization and mutual exclusion mechanisms play a crucial role.
1. The basic concept of Goroutines
In Golang, Goroutines are lightweight threads that can execute concurrently with other Goroutines. Goroutines cost fewer resources to create and destroy than traditional threads and can utilize system resources more efficiently.
Goroutines are created using the keyword "go". The sample code is as follows:
package main import ( "fmt" "time" ) func task1() { for i := 0; i < 5; i++ { fmt.Println("Task 1:", i) time.Sleep(time.Millisecond * 500) } } func task2() { for i := 0; i < 5; i++ { fmt.Println("Task 2:", i) time.Sleep(time.Millisecond * 500) } } func main() { go task1() go task2() time.Sleep(time.Millisecond * 3000) }
In the above code, two Goroutines are created through the keyword "go" and execute the task1()
and task2()
functions respectively. . In the main()
function, wait for 3 seconds through the time.Sleep()
function to ensure that Goroutines have enough time to complete execution.
2. Synchronization and mutual exclusion mechanism of Goroutines
In actual concurrent programming, multiple Goroutines may need to share certain resources. At this time, synchronization and mutual exclusion mechanisms are needed to ensure correct access to resources.
1.1 WaitGroup
WaitGroup is used to wait for the execution of a group of Goroutines to complete. Its functionality is similar to CountDownLatch in Java. The sample code is as follows:
package main import ( "fmt" "sync" "time" ) func task(i int, wg *sync.WaitGroup) { defer wg.Done() fmt.Println("Task", i) time.Sleep(time.Millisecond * 500) } func main() { var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go task(i, &wg) } wg.Wait() fmt.Println("All tasks finished") }
In the above code, a waiting group wg
is created through sync.WaitGroup
. Call wg.Add(1)
before each Goroutine is executed to increment the waiting group counter by 1, indicating that there is a task that needs to wait. After each Goroutine is executed, call wg.Done()
to decrement the waiting group counter by 1, indicating that a task has been completed. Finally, wait for all tasks to be completed through wg.Wait()
.
1.2 Mutex
Mutex is a mutex lock, used to protect shared resources that can only be accessed by one Goroutine at the same time. The sample code is as follows:
package main import ( "fmt" "sync" "time" ) var count int var mutex sync.Mutex func task(i int, wg *sync.WaitGroup) { defer wg.Done() mutex.Lock() defer mutex.Unlock() count++ fmt.Println("Task", i, "count:", count) time.Sleep(time.Millisecond * 500) mutex.Lock() defer mutex.Unlock() count-- } func main() { var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go task(i, &wg) } wg.Wait() fmt.Println("All tasks finished") }
In the above code, a mutex lockmutex
is created through sync.Mutex
. In each Goroutine, access to shared resources is protected by calling a pair of mutex.Lock()
and mutex.Unlock()
. In actual applications, the shared resources that need to be protected can be stored in the structure, and access to the shared resources can be controlled through the mutex lock in the structure.
2.1 Once
Once is used to ensure that a certain piece of code will only be executed once during the running of the program. The sample code is as follows:
package main import ( "fmt" "sync" ) var once sync.Once func task() { fmt.Println("Task executed") } func main() { for i := 0; i < 5; i++ { once.Do(task) } }
In the above code, an Once object once
is created through sync.Once
. In each Goroutine, calling once.Do(task)
ensures that the task()
function will only be executed once during the entire program running time.
2.2 Mutex
Mutex can also be used to implement mutual exclusion. The sample code is as follows:
package main import ( "fmt" "sync" "time" ) var count int var mutex sync.Mutex func task(i int, wg *sync.WaitGroup) { defer wg.Done() mutex.Lock() defer mutex.Unlock() fmt.Println("Task", i) time.Sleep(time.Millisecond * 500) } func main() { var wg sync.WaitGroup for i := 0; i < 5; i++ { wg.Add(1) go task(i, &wg) } wg.Wait() fmt.Println("All tasks finished") }
In the above code, by calling mutex.Lock()
and mutex.Unlock()
to ensure that there can only be one at the same time Goroutine executes the task()
function and accesses shared resources.
Summary
Through the introduction of this article, we have learned about the synchronization and mutual exclusion mechanism of Goroutines in Golang concurrent programming. In actual applications, synchronization and mutual exclusion mechanisms are the key to ensuring correct collaboration between multiple Goroutines. Proper use of synchronization and mutual exclusion mechanisms such as WaitGroup, Mutex, and RWMutex can ensure correct access to shared resources, thereby achieving efficient concurrent programming.
The above is the detailed content of Detailed explanation of the synchronization and mutual exclusion mechanism of Goroutines in Golang concurrent programming. For more information, please follow other related articles on the PHP Chinese website!