Go language concurrency and Parallel Learning notes (ii)

Source: Internet
Author: User
Tags sleep function
This is a creation in Article, where the information may have evolved or changed.

Don't know if you've noticed a phenomenon, or this code, if I run in two goroutines inside the words:

var quit chan int = make (chan int) func loop () {for    I: = 0; i <; i++ {        fmt. Printf ("%d", i)    }    quit <-0}func main () {    //Open two goroutine run function loop, loop function is responsible for printing 10 numbers    go loop ()    go Loop () for    I: = 0; i < 2; i++ {        <-quit    }}

We observe the output:

0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9

Is this a problem??

In the past, when we used a thread to do similar tasks, the thread of the system would be out of the way, showing the output in a random order. And goroutine Why is this output?

is goroutine in parallel?

Let's find an example to test:

Package Mainimport "FMT" import "Time" var quit Chan intfunc foo (id int) {    fmt. PRINTLN (ID) time    . Sleep (time. Second)//Pause one second    quit <-0//Send message: I'm done! }func Main () {    count: =-N-    quit = make (chan int, count)//buffer 1000 data for    I: = 0; i < count; i++ {//Open 1000 A goroutine        go foo (i)    }    for I: =0; i < count; i++ {//waits for all completion messages to be sent.        <-quit    }}

Let's run this program (the reason for compiling and then running is to let the program run as fast as possible, the test results better):

go build test.gotime ./test./test  0.01s user 0.01s system 1% cpu 1.016 total

We see that the total takes close to one second. It looks parallel!

We need to first consider what is concurrency and what is parallel

Parallel and concurrency

Conceptually, concurrency and parallelism are different, so simply look at this picture (the original is from here)

    • Two queues, one coffee machine, that is the concurrency
    • Two queues, two coffee machines, that's parallel

More information: Concurrency is not parallel, of course, there is more on Google on the difference between parallelism and concurrency.

So back to the beginning of the question, from the performance of the above two examples, a plurality of Goroutine run loop function will be goroutine, and sleep is executed together.

Why is this?

By default, all go Goroutines can only run in one thread.

In other words, none of the above two codes are parallel, but they are both concurrent.

If the current goroutine does not block, it will not give up the CPU to other goroutine, so the output in example one will be one goroutine, while the sleep function blocks the current goroutine, The current Goroutine actively allows other goroutine to execute, thus forming a logical parallel, that is, concurrency.

The real parallel

To achieve true parallelism, we need to tell go that we allow up to multiple cores at the same time.

Back to the initial example, we set the maximum open 2 native thread, we need to use the runtime package (runtime package is Goroutine Scheduler):

Import (    "FMT"    "runtime") var quit chan int = make (chan int) func loop () {    for I: = 0; i <; i++ {//For observation, Run more        FMT. Printf ("%d", i)    }    quit <-0}func main () {    runtime. Gomaxprocs (2)//use up to 2 cores    go loop ()    go Loop () for    I: = 0; i < 2; i++ {        <-quit    }}

This will see that two goroutine will output data in a preemptive manner.

We can also explicitly give up CPU time like this:

Func loop () {for    I: = 0; i <; i++ {        runtime. Gosched ()///explicitly yields CPU time to other Goroutine        FMT. Printf ("%d", i)    }    quit <-0}func main () {    go loop ()    go Loop () for    I: = 0; I < 2; i++ {        <-quit    }}

Observe the results to see a regular output:

0 0 1 1 2 2 3 3 4 4 5 5 6 6 7 7 8 8 9 9

In fact, this initiative to give up CPU time is still in the single core run. But the hand site switching Goroutine led to the seemingly "parallel".

In fact, as a Python programmer, goroutine Let me think more of the gevent process, not the native thread.

On the schedule of runtime package to Goroutine, there is a good answer on StackOverflow: http://stackoverflow.com/questions/13107958/ What-exactly-does-runtime-gosched-do

A small problem

I saw this problem in Segmentfault: http://segmentfault.com/q/1010000000207474

The topic says, as the next procedure, according to understand should print next 5 times "world" Yes, but why did not print

Package Mainimport (    "FMT") Func say (s string) {for    I: = 0; i < 5; i++ {        fmt. Println (s)    }}func main () {    go Say ("world")//Open a new Goroutines execute for    {    }}

The answer downstairs is very good, here go is still using a single core, for the dead loop occupies all the resources of the single core CPU, and the main line and say two goroutine are in one thread, so say no chance to execute. The solution is still two:

    • Allow go to use multi-core ( runtime.GOMAXPROCS )

    • Manual explicit Transfer ( runtime.Gosched )

Runtime Scheduler

Runtime Scheduler is a magical thing, but I really hope it does not exist, I hope that the explicit scheduling can be more natural, multi-core processing is turned on by default.

About the runtime package several functions:

    • GoschedLet the CPU out

    • NumCPUReturns the number of CPU cores for the current system

    • GOMAXPROCSSet the maximum number of CPU cores that can be used concurrently

    • GoexitExit current Goroutine (but defer statement will execute as usual)

Summarize

As we can see from the example, by default, all Goroutine will run in a native thread, i.e. only one CPU core is used.

In the same native thread, if the current goroutine does not block, it will not give up the CPU time to other goroutines of the same thread, this is the Go runtime on the Goroutine dispatch, we can also use the runtime package to manually dispatch.

The two examples at the beginning of this article are restricted to single-core CPUs, and all goroutines run in a thread, analyzed as follows:

    • For code example one (the one for the loop function), each goroutine does not have a blockage (until quit flows into the data), so each goroutine does not voluntarily yield the CPU before quit, and serial printing occurs
    • For the code example two (the one of time), each goroutine is blocked when sleep is called, yielding the CPU, so example two executes concurrently.

So, what about when we open multicore? What does the go language do with Goroutine's scheduling behavior?

We can find a word here on the official website of Golang:

When a coroutine blocks, such as by calling a blocking system call, the run-time automatically moves other coroutines on t He same operating system thread to a different, runnable thread so they won ' t is blocked.

Other words:

When a goroutine is blocked, go automatically transfers other goroutines that are in the same system thread as the Goroutine to another system thread so that these goroutines do not block

Open a multicore experiment

There is still a need to do an experiment to test the allocation of goroutines to the native thread under multicore support, and also to verify that we get the conclusion that "Goroutine does not block and does not let go of the CPU".

The experiment code is as follows:

Package Mainimport (    "FMT"    "runtime") var quit chan int = make (chan int) func loop (id int) {//ID: the Goroutine designator 
  for I: = 0; I < 10; i++ {//print 10 times the Goroutine's label        FMT. Printf ("%d", id)    }    quit <-0}func main () {    runtime.  Gomaxprocs (2)//use up to 2 cores for    I: = 0; i < 3; i++ {//Open three Goroutine        go Loop (i)} for    I: = 0; i < 3; i++ {        <-quit    }}

Running several times will see similar output (different machine environments):

0 0 0 0 0 1 1 0 0 1 0 0 1 0 1 2 1 2 1 2 1 2 1 2 1 2 2 2 2 20 0 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 2 2 2 2 2 2 2 2 2 20 0 0 0 0 0 0 1 1 1 1 1 0 1 0 1 0 1 2 1 2 1 2 2 2 2 2 2 2 20 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 0 2 0 2 0 2 2 2 2 2 2 2 20 0 0 0 0 0 0 1 0 0 1 0 1 2 1 2 1 2 1 2 1 2 1 2 1 2 1 2 2 2

Execute it and we will find the following phenomena:

    • Sometimes preemptive output can occur (indicating that go has opened more than one native thread, which is true parallelism)
    • Sometimes sequential output, print 0 and then print 1, and then Print 2 (indicating go to open a native thread, single-threaded goroutine not blocking does not release the CPU)

Then, we will also observe a phenomenon, whether it is the output of the preemption or sequential output, there will be two numbers show such a phenomenon:

    • All outputs of one number are preceded by all outputs of another number

The reason is that 3 goroutine assigned to up to 2 threads, will be assigned to at least two goroutine in the same thread, the single-line thread Goroutine does not block not to release the CPU, also occurred in the sequential output.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.