The sync.Mutex type is the Go implementation of a mutex. Its definition, which can be found in the mutex.go file of the sync directory, is as follows:
// A Mutex is a mutual exclusion lock. // The zero value for a Mutex is an unlocked mutex. // // A Mutex must not be copied after first use. type Mutex struct { state int32 sema uint32 }
The definition of the sync.Mutex type is nothing extraordinary. All of the interesting work is being done by the sync.Lock() and sync.Unlock() functions that can lock and unlock a sync.Mutex mutex, respectively. Locking a mutex means that nobody else can lock it until it has been released using the sync.Unlock() function.
The mutex.go program, which is going to be presented in five parts, illustrates the use of the sync.Mutex type.
The first code segment of mutex.go follows next:
package main import ( "fmt" "os" "strconv" "sync" "time" ) var ( m sync.Mutex v1 int )
The second part of mutex.go is shown in the following Go code:
func change(i int) { m.Lock() time.Sleep(time.Second) v1 = v1 + 1 if v1%10 == 0 { v1 = v1 - 10*i } m.Unlock() }
The critical section of this function is the Go code between the m.Lock() and m.Unlock() statements.
The third part of mutex.go contains the following Go code:
func read() int { m.Lock() a := v1 m.Unlock() return a }
Similarly, the critical section of this function is defined by the m.Lock() and m.Unlock() statements.
The fourth code segment of mutex.go follows next:
func main() { if len(os.Args) != 2 { fmt.Println("Please give me an integer!") return } numGR, err := strconv.Atoi(os.Args[1]) if err != nil { fmt.Println(err) return } var waitGroup sync.WaitGroup
The last part of mutex.go is shown in the following Go code:
fmt.Printf("%d ", read()) for i := 0; i < numGR; i++ { waitGroup.Add(1) go func(i int) { defer waitGroup.Done() change(i) fmt.Printf("-> %d", read()) }(i) } waitGroup.Wait() fmt.Printf("-> %d ", read()) }
Executing mutex.go will generate the following output:
$ go run mutex.go 21 0 -> 1-> 2-> 3-> 4-> 5-> 6-> 7-> 8-> 9-> -30-> -29-> -28-> -27-> -26-> -25-> -24-> -23-> -22-> -21-> -210-> -209-> -209 $ go run mutex.go 21 0 -> 1-> 2-> 3-> 4-> 5-> 6-> 7-> 8-> 9-> -130-> -129-> -128-> -127-> -126-> -125-> -124-> -123-> -122-> -121-> -220-> -219-> -219 $ go run mutex.go 21 0 -> 1-> 2-> 3-> 4-> 5-> 6-> 7-> 8-> 9-> -100-> -99-> -98-> -97-> -96-> -95-> -94-> -93-> -92-> -91-> -260-> -259-> -259
If you remove the m.Lock() and m.Unlock() statements from the change() function, the program will generate output similar to the following:
$ go run mutex.go 21 0 -> 1-> 6-> 7-> 5-> -60-> -59-> 9-> 2-> -58-> 3-> -52-> 4-> -57-> 8-> -55-> -90-> -54-> -89-> -53-> -56-> -51-> -89 $ go run mutex.go 21 0 -> 1-> 7-> 8-> 9-> 5-> -99-> 4-> 2-> -97-> -96-> 3-> -98-> -95-> -100-> -93-> -94-> -92-> -91-> -230-> 6-> -229-> -229 $ go run mutex.go 21 0 -> 3-> 7-> 8-> 9-> -120-> -119-> -118-> -117-> 1-> -115-> -114-> -116-> 4-> 6-> -112-> 2-> -111-> 5-> -260-> -113-> -259-> -259
The reason for such a change in the output is that all goroutines are simultaneously changing the shared variable, which is the main reason that the output appears randomly generated.