Golang entry: goroutine (coroutines)

In the operating system, the executable is an abstract concept. There are processes, threads and coroutines (coroutine) corresponding entities. Coroutines, also known as lightweight threads, compared with conventional processes and threads, coroutines most important feature is the "light"! You can easily create millions of coroutine system resources without causing failure.
Most programming language syntax level does not directly support coroutines, but support through libraries. But with the way the library of support functions is often not complete, such as providing only create lightweight threads, destruction and switching capabilities. If you call a synchronous IO operation in such a coroutine, such as network communications, local file reading and writing, will block coroutine other concurrent execution, making it impossible to achieve a lightweight thread itself hoped to achieve.

goroutine

Golang at the language level support coroutines, called goroutine. All operating system calls Golang standard library (including all synchronized IO operations), CPU will transfer to other goroutine. It makes goroutine switch management is not dependent on the threads and processes of the system, nor on the number of CPU cores, but to run the unified Golang time.

goroutine is the core Golang concurrent design, more about the concept of concurrency, please refer to " Golang Getting Started: Understanding concurrency and parallelism ." The next part of this article focuses on demo presentation by goroutine usage.

Getting demo

To run a function in a coroutine, add keywords go directly when calling the function on it:

package main

import (
    "time"
    "fmt"
)

func say(s string) {
    for i := 0; i < 3; i++ {
        time.Sleep(100 * time.Millisecond)
        fmt.Println(s)
    }
}

func main() {
    go say("hello world")
    time.Sleep(1000 * time.Millisecond)
    fmt.Println("over!")
}

Above code is executed, the output is:

hello world
hello world
hello world
over!

As to why the call in the main function Sleep, How to use instead of Sleep elegant way, refer to " golang Getting Started: Wait goroutine complete the task ," a text.

goroutine life cycle

Let's understand goroutine life cycle through the following demo:

main Package 

Import ( 
    " Runtime " 
    " Sync " 
    " FMT " 
) 

FUNC main () { 
    // assigned to a logical processor scheduler uses 
    runtime.GOMAXPROCS ( . 1 ) 

    // WG to wait for completion of the program
     // count is incremented by 2, He said to wait two goroutine 
    var wg sync.WaitGroup 
    wg.Add ( 2 ) 

    fmt.Println ( " Start goroutines " ) 

    // declare an anonymous function, and create a goroutine 
    Go FUNC () {
         // call the function exits when Done to inform the main function has been completed and 
        the defer wg.Done () 

        // display the alphabet three times
        for COUNT: = 0 ; COUNT < . 3 ; COUNT ++ {
             for  char : = ' A ' ; char < ' A ' + 26 is ; char ++ { 
                fmt.Printf ( " % C " , char ) 
            } 
            fmt.Println () 
        } 
    } () 
    // declare an anonymous function, and create a goroutine 
    Go FUNC () {
         // call Done when the function exits to notify the main function has been completed and 
        the defer wg.Done () 

        //3 alphabet displayed 
        for COUNT: = 0 ; COUNT < 3 ; COUNT ++ {
             for  char : = ' A ' ; char < ' A ' + 26 is ; char ++ { 
                fmt.Printf ( " % C " , char ) 
            } 
            FMT .Println () 
        } 
    } () 

    // wait goroutine end 
    fmt.Println ( " waiting the To Finish " ) 
    wg.Wait ()

    fmt.Println("Terminating Program")
}

At the beginning of the demo, by calling a function GOMAXPROCS runtime package, the number of logical processors that can be used as a set.
Then three times the output of lower case letters and capital letters by two or three times goroutine anonymous function performed respectively. Results of running the above code, the following output:

Start Goroutines
Waiting To Finish
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
a b c d e f g h i j k l m n o p q r s t u v w x y z
a b c d e f g h i j k l m n o p q r s t u v w x y z
a b c d e f g h i j k l m n o p q r s t u v w x y z
Terminating Program

Goroutine before the first time to complete all tasks too short, so that the scheduler switches to the second goroutine, to complete all tasks. This is why the first output'll see all uppercase letters, lowercase letters only after output. Two goroutine we created one by one, to run concurrently, independently displaying the alphabet task.
Because goroutine performed in a non-blocking manner, they will be with the end of the program (main thread) die out, so we use WaitGroup in the main function to wait two goroutine complete their work-related information more WaitGroup, please refer to " golang Getting Started: wait goroutine complete the task ," a text.

Internal scheduler algorithm based on a positive run of goroutine before the end of the work, can be stopped and rescheduled. The purpose scheduler to do this is to prevent a prolonged occupation goroutine logical processors. When goroutine occupied for too long, the scheduler will stop goroutine currently running, and other opportunities goroutine to run can run.
Let us understand this scene through the following diagram (figure comes from the Internet):

  • In step 1, the scheduler runs goroutine A, while waiting to be scheduled goroutine B run queue.
  • In step 2, the scheduler exchange goroutine A and goroutine B. Since goroutine A does not work is completed, so it is put back into the run queue.
  • In step 3, goroutine B has completed its work and system destruction. This has also allowed the work to continue until the goroutine A.

Let's run through a long time of some tasks to observe the behavior, run the following code:

package main

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

func main() {
    // wg用来等待程序完成
    var wg sync.WaitGroup

    // 分配一个逻辑处理器给调度器使用
    runtime.GOMAXPROCS(1)

    // 计数加2,表示要等待两个goroutine
    wg.Add(2)

    // 创建两个goroutine
    fmt.Println("Create Goroutines")
    go printPrime("A", &wg)
    go printPrime("B", &wg)

    // 等待goroutine结束
    fmt.Println("Waiting To Finish")
    wg.Wait()

    fmt.Println("Terminating Program")
}

// printPrime 显示5000以内的素数值
func printPrime(prefix string, wg *sync.WaitGroup){
    // 在函数退出时调用Done来通知main函数工作已经完成
    defer wg.Done()

next:
    for outer := 2; outer < 5000; outer++ {
        for inner := 2; inner < outer; inner++ {
            if outer % inner == 0 {
                continue next
            }
        }
        fmt.Printf("%s:%d\n", prefix, outer)
    }
    fmt.Println("Completed", prefix)
}

代码中运行了两个 goroutine,分别打印 1-5000 内的素数,输出的结果比较长,精简如下:

Create Goroutines
Waiting To Finish
B:2
B:3
...
B:3851          
A:2             ** 切换 goroutine
A:3
...
A:4297          
B:3853          ** 切换 goroutine
...
B:4999
Completed B
A:4327          ** 切换 goroutine
...
A:4999
Completed A
Terminating Program

上面的输出说明:goroutine B 先执行,然后切换到 goroutine A,再切换到 goroutine B 运行至任务结束,最后又切换到 goroutine A,运行至任务结束。注意,每次运行这个程序,调度器切换的时间点都会稍有不同。

让 goroutine 并行执行

前面的两个示例,通过设置 runtime.GOMAXPROCS(1),强制让 goroutine 在一个逻辑处理器上并发执行。用同样的方式,我们可以设置逻辑处理器的个数等于物理处理器的个数,从而让 goroutine 并行执行(物理处理器的个数得大于 1)。
下面的代码可以让逻辑处理器的个数等于物理处理器的个数:

runtime.GOMAXPROCS(runtime.NumCPU())

其中的函数 NumCPU 返回可以使用的物理处理器的数量。因此,调用 GOMAXPROCS 函数就为每个可用的物理处理器创建一个逻辑处理器。注意,从 Golang 1.5 开始,GOMAXPROCS 的默认值已经等于可以使用的物理处理器的数量了。
修改上面输出素数的程序:

runtime.GOMAXPROCS(2)

因为我们只创建了两个 goroutine,所以逻辑处理器的数量设置为 2 就可以了,重新运行该程序,看看是不是 A 和 B 的输出混合在一起了:

...
B:1741
B:1747
A:241
A:251
B:1753
A:257
A:263
A:269
A:271
A:277
B:1759
A:281
...

除了这个 demo 程序,在真实场景中这种并行的方式会带来很多数据同步的问题。接下来我们将介绍如何来解决数据的同步问题。

参考:
《Go语言实战》

Guess you like

Origin www.cnblogs.com/sparkdev/p/10930168.html