简体   繁体   中英

What is meant by race condition in GO when using -race flag

Consider following code:

package main

import (
    "fmt"
    "sync"
)

func main() {
var a int
m := new(sync.Mutex)

wg := sync.WaitGroup{}
wg.Add(2)

go func(){
    m.Lock()
    a = 2
    m.Unlock()
    wg.Done()
}()

go func(){
    //m.Lock()
    a = 9
    //m.Unlock()
    wg.Done()
}()

wg.Wait()

fmt.Println(a)
}

if we run this code with -race flag we get warning that there is a race condition.

1) what can possibly go wrong with this race condition?

if we uncomment lock in second routine we don't get a race condition warning. But we can have different output so a race condition exists.

2) Why now we don't have a race condition warning?

1) what can possibly go wrong with this race condition?

  • Undefined behavior
  • memory corruption
  • crashes Literally the worst possible outcome of not knowing and potentially having a completely invalid value in your variable :p

2) Why now we don't have a race condition warning?

Mutex is a primitive which can guarantee atomicity. When lock is uncommented the runtime/os fully syncrhonizes access to the statement(s) that the lock protects. ie a will never be set to both 2 and 9 at the same time.

There may be a performance implications here (your app may never run into them) because these are serialized operations. This is usually a fine tradeoff because it ensures correctness at the cost of potential performance implications.


The go documentation has amazing resources around the specifics of this issue:

Race condition occurs when two or more operations must execute in correct order,but they don't do so because of the way program is written.

data race: one concurrent operation attempts to read a variable while at some undetermined time another concurrent operation is attempting to write to the same variable.

This simple example have a data race, since we are reading a and printing, where some other go-routine is trying to update the value.

package main

import "fmt"

func main() {
    var a int

    go func() {
        a++
    }()

    if a == 0 {
        fmt.Println("data:", a)
    }
}

output possibilities:

  1. a is 1 ,and it is printed( a is 0 , while entering the if block, but by the time you print it changed.)
  2. a is 0 , and it is printed(update didn't happen yet)
  3. nothing is printed, ( a is updated)

Coming to your question:

1. what can possibly go wrong with this race condition?

Ans:

  1. output of a is undetermined, either it can be 2 or 9 .(if your first go func() runs first, a will be 9 , if second func() runs first, a will be 2 ).
  2. your updating a in second func() , now first func() runs and tries to update, function which wrote last to a will determine the output.It can either be 2 or 9 once again.
  3. locks are used in an assumption that at any time only one goroutine holds it, and when it has access no other variable writes to it because they don't get the exclusive lock, but if any shared variable is not protected by lock then it can access the variable and do necessary changes.

2. Why now we don't have a race condition warning?

Ans:

Because now at any movement of time only one goroutine can access a and update it. But even now a is not deterministic, which ever func() runs last will determine it. (even this a race-condition, one can overcome this with proper synchronization).

-race cannot determine this type of race conditions, because it's almost impossible to do so.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM