• about reply
Alpha Reply Logo
Menu
  • Lösungen
    Lösungen
    • Lösungen
    • Fusion
    • Synapse
  • Insights
    Insights
    • Insights
    • Erfolgreiche Projekte
    • Artikel
    • Events
    • Tutorials
  • Partnerschaften
Choose language:
  • about Reply
Alpha Reply Logo
Focus On

Go concurrency mit Mutex

goconcurrencywithmutex-1408x704.jpg 0

Welcome back to the series tutorials on Go Concurrency. If you missed previous tutorials please refer to: Go Concurrency with GoRoutines.

Today's topic is Mutex which is one of the most critical concepts in concurrent programming. When you use GoRoutine and Channels to perform concurrent programming, have you ever thought about what happens when two GoRoutines try to access the same piece of data in the same memory location? This is the so-called race condition and sometimes it may produce unexpected results which are often hard to debug.

What is Mutex in Golang

Let's have a look at an example of concurrent programming without Mutex.

      package main
 
import (
    "fmt"
    "sync"
)
 
type calculation struct{
    sum int
}
 
func main() {
 
    test := calculation{}
    test.sum = 0
    wg := sync.WaitGroup{}
    for i:=0; i<500; i++{
        wg.Add(1)
        go dosomething(&test, &wg)
    }
    wg.Wait()
    fmt.Println(test.sum)
}
 
func dosomething(test *calculation , wg *sync.WaitGroup) {
    test.sum++
    wg.Done()
}

In the above example, we declare a calculation struct called test and we spawn multiple GoRoutines with the for loop to increase the value of sum by 1. (If you are not familiar with GoRoutines and WaitGroup, please refer to the previous tutorials). We may expect that the value of sum after the for loop should be 500. However, this may not be true. Sometimes, you may obtain a result less than 500 (and never more than 500, of course). The reason behind this is that there is a certain probability that two GoRoutines are manipulating the same variable at the same memory location which causes this unexpected result. The solution to this problem is to use Mutex.

      package main
 
import (
    "fmt"
    "sync"
)
type calculation struct {
    sum   int
    mutex sync.Mutex
}
 
func main() {
 
    test := calculation{}
    test.sum = 0
    wg := sync.WaitGroup{}
    for i := 0; i < 500; i++ {
        wg.Add(1)
        go dosomething(&test, &wg)
    }
 
    wg.Wait()
    fmt.Println(test.sum)
}
 
func dosomething(test *calculation, wg *sync.WaitGroup) {
    test.mutex.Lock()
    test.sum++
    test.mutex.Unlock()
    wg.Done()
}      

In the second example, we add a mutex attribute inside the struct which is a type of sync.Mutex. We then use the Lock() and Unlock() of mutex to protect test.sum when it is being modified concurrently, i.e. test.sum++.

Keep in mind that using mutex is not without its consequences as it will impact the performance of your application, and therefore we need to use it appropriately and efficiently. If your GoRoutines only read the shared data and not write to the same data, then race condition will not be a concern. In this case, you can use RWMutex instead of Mutex to improve performance time.

Keyword Defer

Using the defer keyword for the Unlock() is usually a good practice.

      func dosomething(test *calculation) error{
    test.mutex.Lock()
    defer test.mutex.Unlock()
 
    err1 :=...
    if err1 != nil {
        return err1
    }   
    err2 :=...
    if err2 != nil {
        return err2
    }
    // ... do more stuff ...  
    return nil
}
      

In this case, we have multiple if err!=nil which may lead to early exiting of the function. By using defer, we are guaranteed to release the lock no matter how the function returns. Otherwise, we need to put Unlock() in every place that the function may possibly return. However, it does not mean we should use defer all the time. Let's have a look at another example.

      func dosomething(test *calculation){
    test.mutex.Lock()
    defer test.mutex.Unlock()
 
    // modify the variable which requires mutex protect
    test.sum =...
 
    // perform a time consuming IO operation
    http.Get()
}          

In this example, mutex will not release the lock until the time consuming function (which is http.Get() here) finishes. In this case we can just Unlock the mutex after the line test.sum=... because this is the only place where we manipulate the variable.

We hope you enjoyed this tutorial series of Go Concurrency. In our next tutorial, we will show a couple of actual use cases combining all these components we have introduced in the past weeks.

Stay tuned!

RELATED CONTENTS

Alpha Reply & yes®

Mit dem Beginn des neuen Jahres freuen wir uns bekannt geben zu können, dass Alpha Reply und yes.com erfolgreich eine strategische Partnerschaft geschlossen haben.

Go Concurrency mit WaitGroup 0

Go Concurrency mit WaitGroup

In diesem Tutorial hilft Alpha Reply dabei, das Konzept der WaitGroup zu verstehen. In einigen Szenarien müssen bestimmte Teile des Codes blockiert werden, damit diese GoRoutinen entsprechend Ihren Anforderungen ausgeführt werden können.

Go Concurrency mit Channels

In diesem Tutorial hilft Alpha Reply dabei, das Konzept der Kanäle zu verstehen. Während einer Concurrency-Programmierung spielt die Kommunikation in einer gemeinsam genutzten Speicherumgebung eine wichtige Rolle in der Synchronisierung des gesamten Programms. In Go ist Channels eine integrierte Funktion für Synchronisationen.

Go Concurrency mit Channels 0
 
 
 
 
Reply ©​​ 2023​ - ​​​​Datenschutz​​​ - Impressum
Cookie-Einstellungen​
  • Über Reply
  • Investoren​​
  • Newsroom
  • Folgen Sie uns auf​​​​​​​​


  • ​​
  • ​
​
  • ​​​​Company Information
  • ​​​​Datenschutz- und Cookie Richtlinie
  • Datenschutzhinweis (Kunden)
  • Datenschutzhinweis (Dienstleister)
  • Datenschutzhinweis (Bewerber)
​Reply Enterprise Social Network​