In the below code:
package main
import "fmt"
func main() {
for i := 0; i <= 9; i++ {
go func() {
fmt.Println(i)
}()
}
}
Output:
code$
code$ go install github.com/myhub/cs61a
code$ bin/cs61a
code$
Above program does not provide any output.
1) Is their a data race for single memory location i among 10 go-routines?
2) Why above code does not print value of free variable i?
Is there a data race?
Yes, confirm it by running go run -race example.go. The main goroutine writes i, and the other goroutines read it without any synchronization. See Passing parameters to function closure; Why do these two for loop variations give me different behavior? and Register multiple routes using range for loop slices/map.
Why above code does not print anything?
Because when the main goroutine ends, your program ends as well. It does not wait for other non-main goroutines to finish. See No output from goroutine
Fix
Make a copy of the loop variable, and use that in the closures, and use a sync.WaitGroup to wait for the launched goroutines to end:
var wg sync.WaitGroup
for i := 0; i <= 9; i++ {
i2 := i
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println(i2)
}()
}
wg.Wait()
This will output (try it on the Go Playground):
9
0
2
1
6
5
3
7
8
4
An alternative is to pass i as a parameter to the launched function:
var wg sync.WaitGroup
for i := 0; i <= 9; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
fmt.Println(i)
}(i)
}
wg.Wait()
Try this one on the Go Playground.
Related
Is there any way to make goroutine execute one after another ( one by one) if it was the same function?
I didn't mean to use goroutine firstly. However, "os/exec" function in TCP will cause the tcp forced to stop. Thus I use goroutine to avoid crashing. But I still want them to execute by order, instead of concurrently. Here is my code.
func handleTCP(conn net.Conn) {
defer conn.Close()
fmt.Println("handle TCP function")
for {
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
cmdArgs := []string{temp_str, test_press, gh, "sample.csv"}
cmd := exec.Command("calib.exe", cmdArgs...)
wg.Done()
}()
}
}
You can use locks to limit access to a resource to one thread at a time as follows
i := 1
iLock = &sync.Mutex{}
for {
go func() {
iLock.Lock()
fmt.Printf("Value of i: %d\n", i)
iLock.Unlock()
}
}
More examples here
Not sure what you are using the WaitGroup for. If it is to achieve the sequentiality you desire, then it can be removed.
Try to put the lock into function, it makes theirs execution sequential. But remember about wg.Done() must be under defer at first line of the function.
Something like this:
var mu sync.Mutex
func handleTCP(conn net.Conn) {
defer conn.Close()
fmt.Println("handle TCP function")
for {
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
defer wg.Done()
mu.Lock()
defer mu.UnLock()
cmdArgs := []string{temp_str, test_press, gh, "sample.csv"}
cmd := exec.Command("calib.exe", cmdArgs...)
}()
}
}
I'm new to Go so please excuse my ignorance. I'm attempting to iterate through a bunch of wordlists line by line indefinitely with goroutines. But when trying to do so, it does not iterate or stops half way through. How would I go about this in the proper manner without breaking the flow?
package main
import (
"bufio"
"fmt"
"os"
)
var file, _ = os.Open("wordlist.txt")
func start() {
scanner := bufio.NewScanner(file)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}
func main(){
for t := 0; t < 150; t++ {
go start()
fmt.Scanln()
}
}
Thank you!
You declare file as a global variable. Sharing read/write file state amongst multiple goroutines is a data race and will give you undefined results.
Most likely, reads start where the last read from any of the goroutines left off. If that's end-of-file, it likely continues to be end-of-file. But, since the results are undefined, that's not guaranteed. Your erratic results are due to undefined behavior.
Here's a revised version of your program that declares a local file variable and uses a sync.Waitgroup to synchronize the completion of all the go start() goroutines and the main goroutine. The program checks for errors.
package main
import (
"bufio"
"fmt"
"os"
"sync"
)
func start(filename string, wg *sync.WaitGroup, t int) {
defer wg.Done()
file, err := os.Open(filename)
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
lines := 0
scanner := bufio.NewScanner(file)
for scanner.Scan() {
lines++
}
if err := scanner.Err(); err != nil {
fmt.Println(err)
return
}
fmt.Println(t, lines)
}
func main() {
wg := &sync.WaitGroup{}
filename := "wordlist.txt"
for t := 0; t < 150; t++ {
wg.Add(1)
go start(filename, wg, t)
}
wg.Wait()
}
This prints 0.
But when I add empty fmt.Println() in for loop its prints non zero value.
Any ideas why?
GOMAXPROCS=1 ./foo
0
package main
import (
"log"
"os"
"time"
)
func main() {
var i uint64
t := time.AfterFunc(time.Second*1, func() {
log.Println(i)
os.Exit(0)
})
defer t.Stop()
for {
i++
}
}
You have a data race: you access the same variable from multiple goroutines without synchronization. The results are undefined. Use proper synchronization.
It's true that you don't launch goroutines in your code, but quoting from time.AfterFunc():
AfterFunc waits for the duration to elapse and then calls f in its own goroutine.
So you have the main goroutine incrementing (writing) the variable i, and you'll have another goroutine executing the function you pass to time.AfterFunc() which will read i.
Example to use synchronization:
var (
mu sync.Mutex
i uint64
)
t := time.AfterFunc(time.Second*1, func() {
mu.Lock()
log.Println(i)
mu.Unlock()
os.Exit(0)
})
defer t.Stop()
for {
mu.Lock()
i++
mu.Unlock()
}
I have the following for-select structure in code:
go func(runCh chan Caller, shutdownSignal chan bool) {
for {
select {
case request := <-runCh:
go func() {
w.Run(&request)
}()
case <-shutdownSignal:
w.Shutdown()
return
}
}
}(runCh, shutdownCh)
Will I have some problems with this part:
case request := <-runCh:
go func() {
w.Run(&request)
}()
?
If yes, then why?
In other words - does Using goroutines on loop iterator variables part of Common Mistakes also apply to my case and why it does/does not apply here?
No (does not apply here), you have new variable (memory address) on each loop iteration:
case request := <-runCh:
Because this := creates new variable distinct from previous one, proof:
package main
import (
"fmt"
"time"
)
func main() {
runCh := make(chan int, 2)
runCh <- 1
runCh <- 2
for i := 1; i <= 2; i++ {
select {
case request := <-runCh:
go func() {
fmt.Println(request, &request)
time.Sleep(200 * time.Millisecond)
fmt.Println(request, &request)
}()
}
}
time.Sleep(500 * time.Millisecond)
}
Output (the address of request in each loop iteration is different):
1 0xc0000b8000
2 0xc0000b8008
1 0xc0000b8000
2 0xc0000b8008
See: 0xc0000b8000 != 0xc0000b8008
I try to write a complex program with parallel goroutines. It is my first program with channels ;) Each goroutine returns an array and, unfortunately, the result is "random". If I run 10 times the program, I have 10 different results :(
This is an over simplification of my program, the results is good (maybe because it is too simple) but when I run it with -race argument, there is 4 data races.
I tried to had a close() function but it did not worked.
May you help me to find the mistake ? Thank you very much in advance !
package main
import "fmt"
import "sync"
import "strconv"
func cat_strings(a int, b string) []string{
var y []string
j := strconv.Itoa(a)
y = append(y, j)
y = append(y, b)
return y
}
func main() {
var slice []string
var wg sync.WaitGroup
var x []string
queue := make(chan []string, 10)
wg.Add(10)
for i := 0; i < 10; i++ {
go func(i int) {
defer wg.Done()
x = cat_strings(i, "var")
queue <- x
}(i)
}
//close(queue)
go func() {
defer wg.Done()
for t := range queue {
slice = append(slice, t...)
}
}()
wg.Wait()
fmt.Println(slice)
}
There's two pieces to this fix, don't share the slices between goroutines, and then range over queue synchronously in main.
import (
"fmt"
"strconv"
"sync"
)
func cat_strings(a int, b string) []string {
var y []string
j := strconv.Itoa(a)
y = append(y, j)
y = append(y, b)
return y
}
func main() {
var slice []string
var wg sync.WaitGroup
queue := make(chan []string, 10)
wg.Add(10)
for i := 0; i < 10; i++ {
go func(i int) {
defer wg.Done()
queue <- cat_strings(i, "var")
}(i)
}
go func() {
wg.Wait()
close(queue)
}()
for t := range queue {
slice = append(slice, t...)
}
fmt.Println(slice)
}
There's no reason for the extra x slice you're sharing between goroutines. If each goroutine needs another slice, define a new one for each. Sharing a single slice is always going to require extra synchronization.
The other race is between the goruoutine appending from queue to the slice slice, and the final fmt.Println. There's no reason for those be concurrent since you don't want to print until all values have been read, so finish the for-range loop entirely before printing the final value.