golang ---- concurrent parallel &&

Look at the example

package main

import (
	"fmt"
	"runtime"
	"sync"
)

func main() {
	runtime.GOMAXPROCS(1)
	wg := sync.WaitGroup{}
	wg.Add(20)
	for i := 0; i < 10; i++ {
		go func() {
			fmt.Println("go routine 1 i: ", i)
			wg.Done()
		}()
	}
	for i := 0; i < 10; i++ {
		go func(i int) {
			fmt.Println("go routine 2 i: ", i)
			wg.Done()
		}(i)

	}
	wg.Wait()
}

  Output:

go routine 2 i: 9

go routine 1 i: 10

go routine 1 i: 10

go routine 1 i: 10

go routine 1 i: 10

go routine 1 i: 10

go routine 1 i: 10

go routine 1 i: 10

go routine 1 i: 10

go routine 1 i: 10

go routine 1 i: 10

go routine 2 i: 0

go routine 2 i: 1

go routine 2 i: 2

go routine 2 i: 3

go routine 2 i: 4

go routine 2 i: 5

go routine 2 i: 6

go routine 2 i: 7

go routine 2 i: 8

 Why is this result?

Concurrency is not equal to the parallel

golang core developer Rob Pike specifically mentioned this topic (are interested can look at this video or see the original PPT )

Although we have used to create a goroutine go in for loop, we take it for granted think each cycle variables, golang will perform this goroutine, then that time is output variables. At this point, we fall into the mindset. The default of the parallel concurrently.

Indeed, goroutine go through the creation of concurrent execution of which will function code. But it will execute it in accordance with each cycle time as we have in mind? the answer is negative!

Rob Pike specifically mentioned golang concurrent refer to certain structures in the function code logic can run simultaneously, but physically on may not be run simultaneously. Refers in parallel at the physical level is performed using a different CPU in the same or different tasks.

golang of goroutine scheduling model determines, for each goroutine is running in a virtual CPU is (that is, the number of virtual CPU we (1) set by runtime.GOMAXPROCS). The number of virtual CPU may not match the actual number of CPU. Each goroutine will be a particular P (virtual CPU) is selected to maintain, and M (physical computing resources) every time he returns to pick a valid P, then P execution of goroutine.

Each P will be maintained their goroutine G into a queue, including the goroutine stack information, information and the like is executable. By default, the number P is equal to the actual physical CPU. So when we create goroutine through the loop, it will be assigned to each goroutine P different queues. While the number of M are not unique, randomly selected when the M P, it might just randomly selected goroutine.

In this problem, we set P = 1. So all goroutine will be bound to the same P in. If we modify the value runtime.GOMAXPROCS, you will see another order. If we output goroutine id, you can see the effect of randomly selected:

package main

import (
	"fmt"
	"runtime"
	"strconv"
	"strings"
	"sync"
)

func main() {
	wg := sync.WaitGroup{}
	wg.Add(20)
	for i := 0; i < 10; i++ {
		go func() {
			var buf [64]byte
			n := runtime.Stack(buf[:], false)

			idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]

			id, err := strconv.Atoi(idField)
			if err != nil {
				panic(fmt.Sprintf("cannot get goroutine id: %v", err))
			}
			fmt.Printf("go routine 1 : i -- %d  id-- %d\n ", i, id)
			wg.Done()
		}()
	}
	for i := 0; i < 10; i++ {
		go func(i int) {
			var buf [64]byte
			n := runtime.Stack(buf[:], false)
			idField := strings.Fields(strings.TrimPrefix(string(buf[:n]), "goroutine "))[0]
			id, err := strconv.Atoi(idField)
			if err != nil {
				panic(fmt.Sprintf("cannot get goroutine id: %v", err))
			}
			fmt.Printf("go routine 2 : i -- %d  id-- %d \n", i, id)
			wg.Done()
		}(i)

	}
	wg.Wait()
}

  Output:

go routine 1 : i -- 10  id-- 20
 go routine 1 : i -- 10  id-- 19
 go routine 1 : i -- 10  id-- 22
 go routine 1 : i -- 10  id-- 25
 go routine 1 : i -- 10  id-- 28
 go routine 2 : i -- 8  id-- 37 
go routine 1 : i -- 10  id-- 23
 go routine 1 : i -- 10  id-- 21
 go routine 2 : i -- 0  id-- 29 
go routine 2 : i -- 9  id-- 38 
go routine 2 : i -- 1  id-- 30 
go routine 2 : i -- 5  id-- 34 
go routine 2 : i -- 2  id-- 31 
go routine 2 : i -- 6  id-- 35 
go routine 2 : i -- 3  id-- 32 
go routine 1 : i -- 10  id-- 24
 go routine 2 : i -- 7  id-- 36 
go routine 1 : i -- 10  id-- 27
 go routine 2 : i -- 4  id-- 33 
go routine 1 : i -- 10  id-- 26

 

We go back to this question, although in the cycle go by the definition of a goroutine. But we said, and not equal to concurrent parallel. Therefore, although the definition, but now it will not necessarily be to do it. After waiting for M select P, in order to perform goroutine. About how golang in goroutine is scheduled (GPM model), you can refer to the Scalable Go Scheduler Design Doc or LearnConcurrency

At this time you should be able to understand why the first output goroutine2 then output goroutine1 it.

Here we are 10 to explain why the goroutine1 in output.

How goroutine bind variables

In golang for loop, golang each use the same instance variables (i.e. title used i). And between golang be shared environment variable is.

When the dispatcher to this goroutine, it directly reads the variable address saved, this time there will be a problem: goroutine saved only variable address, so variables are likely to be modified .

Combined with problems in the for loop, each time using a variable address is the same, that is to say every time i changes, by the end of the cycle, i became 10. And also saved goroutine only i memory address only, so when goroutine1 been done, do not hesitate to put i read out, how much? 10!

But why goroutine2 not 10 it?

Conversely goroutine2, it is easy to understand. Because each loop regenerate a new variable, and then save each goroutine is the address of each new variable. Mutual interference between these variables will not be tampered with by anyone. Therefore, when output, will be from 0 - 9 are sequentially output.

 

 

Guess you like

Origin www.cnblogs.com/saryli/p/11645879.html