How does goroutine schedule work with GOMAXPROCS? - go

I am so confused by goroutines.
Here is the code
func main() {
// runtime.GOMAXPROCS(1)
go spinner(100 * time.Millisecond)
const n = 45
fibN := fib(n) // slow
fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN)
}
func spinner(delay time.Duration) {
for {
for _, r := range `-\|/` {
fmt.Printf("\r%c", r)
time.Sleep(delay)
}
}
}
func fib(x int) int {
if x < 2 {
return x
}
return fib(x-1) + fib(x-2)
}
That is a simple goroutine tutorial code which using goroutine to show an ASCII animation when calculating Fibonacci.
When I set GOMAXPROCS to 1, I think that there will be only one thread to execute goroutine and the Fibonacci function doesn't have any point to yield to animation goroutine. But this demo still works. It shows animation while doing calculating.
How does Go do this without goroutine switching?

Among others : the compiler inserts potential switch points at each function call, so each recursive call to fib(...) can yield to the "spinner" goroutine.
If you try to implement fib without any function call for example :
// note : this is a truly horrific way to compute the Fibonacci sequence,
// don't do this at home
// simulate the "compute Fibonacci recursively" algorithm,
// but without any function call
func fib(n int) int {
var res = 0
var stack []int
stack = append(stack, n)
for len(stack) > 0 {
// pop :
n = stack[len(stack)-1]
stack = stack[0 : len(stack)-1]
if n < 2 {
res += n
continue
}
// else : push 'n-1' and 'n-2' on the stack
stack = append(stack, n-1, n-2)
}
return res
}
https://play.golang.org/p/pdoAaBwyscr
you should see your spinner 'stuck'

Related

Need help about concurrency programming in Go

For this task i need to find min sum in list of numbers. Then i must print number that have min sum. This must be done with Mutex and WaitGroups. I can't find where is the mistake or why is output different.
Logic: Scanf n and make vector with len(n). Then create funcion for sum of number and forward that function to second where we in one FOR cycle give goroutines function to.
I run this code a few times, and sometimes give different answer for same input.
Input:
3
13
12
11
Output:
Sometimes 12
Sometimes 11
package main
import (
"fmt"
"math"
"runtime"
"sync"
)
var wg sync.WaitGroup
var mutex sync.Mutex
var vector []int
var i int
var n int
var firstsum int
var p int //Temp sum
var index_result int
func sumanajmanjih(broj int) int {
var br int
var suma int
br = int(math.Abs(float64(broj)))
suma = 0
for {
suma += br % 10
br = br / 10
if br <= 0 {
break
}
}
return suma
}
func glavna(rg int) {
var index int
firstsum = sumanajmanjih(vector[0])
for {
mutex.Lock()
if i == n {
mutex.Unlock()
break
} else {
index = i
i += 1
mutex.Unlock()
}
fmt.Printf("Procesor %d radni indeks %d\n", rg, index)
p = sumanajmanjih(vector[index])
if p < firstsum {
firstsum = p
index_result = index
}
}
wg.Done()
}
func main() {
fmt.Scanf("%d", &n)
vector = make([]int, n)
for i := 0; i < n; i++ {
fmt.Scanf("%d", &vector[i])
}
fmt.Println(vector)
brojGR := runtime.NumCPU()
wg.Add(brojGR)
for rg := 0; rg < brojGR; rg++ {
go glavna(rg)
}
wg.Wait()
fmt.Println(vector[index_result])
}
Not a full answer to your question, but a few suggestions to make code more readable and stable:
Use English language for names - glavna, brojGR are hard to understand
Add comments to code explaining intent
Try to avoid shared/global variables, especially for concurrent code. glavna(rg) is executed concurrently, and you assign global i and p inside that function, that is a race condition. Sends all the data in and out into function explicitly as argument or function result.
Mutex easily can lock the code, and it is complicated to debug. Simplify its usage. Often defer mutex.Unlock() in the next line after Lock() is good enough.

Why am I getting deadlock and how to fix it (synch)?

Getting deadlock somewhere. What is causing it and how should it be fixed?
Trying to create a molecule creation. Function Make takes input as a string:
func testMolecule() {
water := New()
water.Make("HOH") // water.Make("OOHHHH")
fmt.Println(water.Molecules())
// Output: 1
}
Function should return number of molecules created. Molecule should have 1 O-molecule and 2 H-molecule.
Produce function shouldn't be done till all molecules are created.
import (
"sync"
)
// Water structure holds the synchronization primitives and
// data required to solve the water molecule problem.
// moleculeCount holds the number of molecules formed so far.
// result string contains the sequence of "H" and "O".
// wg WaitGroup is used to wait for goroutine completion.
type Water struct {
sync.Mutex
wg sync.WaitGroup
cond *sync.Cond
moleculeCount int
result string
assignedO int
assignedH int
finished bool
}
// New initializes the water structure.
func New() *Water {
water := &Water{
moleculeCount: 0,
result: "",
assignedO: 0,
assignedH: 0,
finished: false,
}
water.cond = sync.NewCond(water)
return water
}
// releaseOxygen produces one oxygen atom if no oxygen atom is already present.
// If an oxygen atom is already present, it will block until enough hydrogen
// atoms have been produced to consume the atoms necessary to produce water.
//
// The w.wg.Done() must be called to indicate the completion of the goroutine.
func (w *Water) releaseOxygen() {
defer w.wg.Done()
for {
w.Lock()
if w.assignedO == 0 {
w.assignedO = 1
}
for !(w.assignedH == 2) {
w.cond.Wait()
}
w.result = w.result + "O"
w.cond.Broadcast()
w.Unlock()
}
}
// releaseHydrogen produces one hydrogen atom unless two hydrogen atoms are already present.
// If two hydrogen atoms are already present, it will block until another oxygen
// atom has been produced to consume the atoms necessary to produce water.
//
// The w.wg.Done() must be called to indicate the completion of the goroutine.
func (w *Water) releaseHydrogen() {
defer w.wg.Done()
for {
w.Lock()
if w.assignedH < 2 {
w.assignedH = w.assignedH + 1
}
for !(w.assignedO == 1) && w.assignedH == 2 {
w.cond.Wait()
}
w.result = w.result + "HH"
w.cond.Broadcast()
w.Unlock()
}
}
// produceMolecule forms the water molecules.
func (w *Water) produceMolecule(done chan bool) {
for {
w.Lock()
for w.assignedO == 1 && w.assignedH == 2 && !w.finished {
w.cond.Wait()
}
if w.assignedO == 0 && w.assignedH == 0 && w.finished {
w.Unlock()
break
}
w.moleculeCount = w.moleculeCount + 1
w.assignedH = 0
w.assignedO = 0
w.cond.Broadcast()
w.Unlock()
}
done <- true
}
func (w *Water) finish() {
w.Lock()
w.finished = true
w.cond.Broadcast()
w.Unlock()
}
func (w *Water) Molecules() int {
return w.moleculeCount
}
// Make returns a sequence of water molecules derived from the input of hydrogen and oxygen atoms.
func (w *Water) Make(input string) string {
done := make(chan bool)
go w.produceMolecule(done)
for _, ch := range input {
w.wg.Add(1)
switch ch {
case 'O':
go w.releaseOxygen()
case 'H':
go w.releaseHydrogen()
}
}
w.wg.Wait()
w.finish()
<-done
return w.result
}
Function should return number of molecules created. Molecule should have 1 O-molecule and 2 H-molecule.
You don't need any synchronization or concurrency for that. Just count the Os, the Hs, and return the lesser of number of O or half number of H.
https://play.golang.org/p/3FaJDMi5ER7
I know from the previous question that you probably are trying to build some sort of simulation or model, but I don't understand your goals for that.

Why my goroutine stopped after a few moments running?

package main
import (
"fmt"
"runtime"
)
func main() {
runtime.GOMAXPROCS(1)
go spinner()
const n = 45
fibN := fib(n)
fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN)
}
func spinner() {
for {
for _ = range `-\|/` {
}
}
}
func fib(x int) int {
fmt.Println(x)
if x < 2 {
return x
}
return fib(x-1) + fib(x-2)
}
After a few seconds, it stopped printing x in fib anymore. I deliberately use 1 processor to understand why it stops. Is the scheduler stuck and not rescheduling the goroutine running the fib anymore?
Is the scheduler stuck and not rescheduling the goroutine running the fib anymore?
Short answer: yes.
Longer answer: you aren't guaranteed that this will happen, even with just one CPU. The system could do an occasional reschedule on its own. It's just that the implementation on which you are testing this, doesn't.
As JimB notes in a comment, the kind of hard-spin-loop you have in spinner is ... unfriendly, at best. It should contain some method of releasing computing resources, such as spinning with time.After or time.NewTicker, or at the very least, call runtime.Gosched(). I imagine your original spinner was something like this, only without the time.NewTicker:
func spinner() {
t := time.NewTicker(50 * time.Millisecond)
for {
for _, c := range `-\|/` {
fmt.Print(string(c), "\b")
<-t.C
}
}
}
With the ticker in place, the spinner spins pretty fast, but the program completes (despite the non-caching fib). Of course a smarter fib helps rather more:
var fibcache = map[int]int64{}
func fib(x int) int64 {
if x < 2 {
return int64(x)
}
r, ok := fibcache[x]
if !ok {
r = fib(x-1) + fib(x-2)
fibcache[x] = r
}
return r
}
(I changed the return type to int64 for what I hope are obvious reasons.) Now we can find Fibonacci(90) = 2880067194370816120 in 0.00 seconds (rounded).

Explain:function returns same function in go

func main() {
go spinner(100 * time.Millisecond)
const n = 45
fibN := fib(n) // slow
fmt.Printf("\rFibonacci(%d) = %d\n", n, fibN)
}
func spinner(delay time.Duration) {
for {
for _, r := range `-\|/` {
fmt.Printf("\r%c", r)
time.Sleep(delay)
}
}
}
func fib(x int) int {
if x < 2 {
return x
}
return fib(x-1) + fib(x-2)
}
can you explain above fib function ,how the results are obtained.
fib function return a fib calls ,how does the end results come?
The key is in this function:
func fib(x int) int {
if x < 2 {
return x
}
return fib(x-1) + fib(x-2)
}
If x<2 the function returns immediately, if not it retrieves the result from a call to fib with a smaller value of x
For recursive calls there are the 3 Laws of Recursion:
A recursive algorithm must have a base case.
A recursive algorithm
must change its state and move toward the base case.
A recursive
algorithm must call itself, recursively.
http://interactivepython.org/courselib/static/pythonds/Recursion/TheThreeLawsofRecursion.html
In your example, the base case is when x < 2. The state change is the reduction by 1 or 2 and your function calls itself recursively so the three laws are met.

communicating with n goroutines

I am trying to implement fibonacci recursion in golang using n goroutines with communicating via channels.
I am returning an integer from the function, but i am actually just sending the sum of f(n-1) +f(n-2) over channel c but this is not working correctly. It prints the first two values correct, and every value after is just 1.
package main
import "fmt"
// Fibonacci in a recursive version
func fiboR(n int, c chan int ) int {
if(n == 0){
c <- 0
return 0
} else if n == 1 {
c <- 1
return 1
} else{
c <- fiboR(n-1,c) + fiboR(n-2,c)
return fiboR(n-1,c) + fiboR(n-2,c)
}
}
func main() {
for i := 0; i < 10; i++ {
procchan := make(chan int)
go fiboR(i,procchan)
fmt.Println(i,<-procchan )
}
}
Also is it possible to use channels for receiving the two recursive calls?
Your solution will try to output more than the one value you extract from the channel as you increase the value of i.
What your code will try to send to the channel for each i:
0: 0
1: 1
2: 1,0,1
3: 1,0,1,1,2
4: 1,0,1,1,2,1,0,1,3
...
Since you create a new channel for each i and then only extract one value you will always get the first value in the line above.
If you try to run it with these modifications it will output what you wanted (https://play.golang.org/p/_mn3l5x8iZ).
package main
import "fmt"
// Fibonacci in a recursive version
func fiboRchan(n int, c chan int) {
c <- fiboR(n)
}
func fiboR(n int) int {
if n == 0 {
return 0
} else if n == 1 {
return 1
} else {
return fiboR(n-1) + fiboR(n-2)
}
}
func main() {
for i := 0; i < 10; i++ {
procchan := make(chan int)
go fiboRchan(i, procchan)
fmt.Println(i, <-procchan)
}
}
Adding to #nissefors answer, the main process there is most likely a sequential one because in the for loop you would be waiting on the channel to return and then proceed to the next iteration.
A minor modification in the main function could fire all the fibonaccis at once and then in a separate for loop the channels that are corresponding to each go routine can be accessed
Playground URL: https://play.golang.org/p/7e3JnWeSp6
package main
import "fmt"
// Fibonacci in a recursive version
func fiboRchan(n int, c chan int) {
fmt.Println("PROCESSING FOR %d", n)
c <- fiboR(n)
}
func fiboR(n int) int {
if n == 0 {
return 0
} else if n == 1 {
return 1
} else {
return fiboR(n-1) + fiboR(n-2)
}
}
func main() {
var arr[10]chan int
for i := 0; i < 10; i++ {
procchan := make(chan int)
arr[i] = procchan
go fiboRchan(i, procchan)
}
// By now all the go routines are fired
// Now iterate through the channel array and read from the
// respective channel
for i:=0; i< 10; i++ {
fmt.Println(i, <-arr[i])
}
}

Resources