goroutine,channel

Go language has a concept called goroutine, we know that a similar thread, but lighter.
The following program, we serially to perform two loop functions:

package main

import "fmt"

func main() {
    loop()
    loop()
}

func loop() {
    for i := 0; i < 10; i++ {
        fmt.Printf("%d ", i)
    }
}

Undoubtedly, the output would be something like:

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

Let's put a loop on a goroutine ran, we can use the keyword go to define and start a goroutine, multiple runs:

0 1 2 3 4 5 6 7 8 9 
//或有可能是下面这样
0 1 2 3 4 5 6 7 8 9 0 1 2 
//亦或是下面这样
0 1 2 3 4 5 6 7 8 9 0 

We have repeatedly run the above code will find the above results will be similar to this, but just could not complete the output 0-9 twice, obviously we ran a trip mainline, also opened a goroutine to trip ah.

It turned out that not enough time to run in goroutine loop when the main function has been pulled out, the so-called "skin (the main thread), what can the hair (child thread) Yan will be attached."

main function exits too quickly, we have to find a way to stop it prematurely exit a main way is to wait for it:

package main

import (
    "fmt"
    "time"
)

func main() {
    go loop() //启动一个goroutine
    loop()
    time.Sleep(time.Second) //停顿一秒
}

func loop() {
    for i := 0; i < 10; i++ {
        fmt.Printf("%d ", i)
    }
}

This output does two trips, the objective was achieved.
But wait adopted approach is not good, if at the end of goroutine, tell the next mainline said "Hey, I'll run over!" Like the so-called blocking the main line of approach, we wait for all threads in Python finished under recall wording:

for thread in threads:
    thread.join()

Yes, we need to join a similar thing to live blocking the main line. That's channel

Nobumichi

What channels are? Simply put, it is something between goroutine communicate with each other. Similar pipes on our Unix (can pass messages between processes), and to receive messages between the messaging goroutine. In fact, that is, to do the memory shared between goroutine.
Make use of a channel to establish:

var channel chan int = make(chan int)

That message is as Hexiang Xin Road deposit and get news? one example:

package main

import "fmt"

func main() {
    var messages = make(chan string)
    go func(message string) {
        messages <- message //存消息
    }("Ping!")

    fmt.Println(<-messages) //取消息
}

By default, the message store channel and take messages are blocked (called the unbuffered channel, but to understand the concept of buffer later, saying the problem of traffic congestion).

That is, unbuffered channel message and the time taken will be stored pending messages currently in goroutines, unless the other end is ready.
For example, the following main functions and function foo:

package main

var ch = make(chan int)

func foo() {
    ch <- 0 //向ch中加数据,如果没有其他goroutine来取走这个数据,那么挂起foo, 直到main函数把0这个数据拿走
}
func main() {
    go foo()
    <-ch //从ch取数据,如果ch中还没放数据,那就挂起main线,直到foo函数中放数据为止
}

Since the channel that can block the current goroutine, then back to the question on the part of "goroutine" encountered "goroutine tell how I finished the main" problem by using a channel to tell the main line to:

package main

import "fmt"

var ch = make(chan int)

func loop() {
    for i := 0; i < 10; i++ {
        fmt.Printf("%d ", i)
    }
    ch <- 0 //执行完毕了,发个消息
}

func main() {

    go loop()
    <- ch //main在此阻塞住,直到线程跑完, 取到消息.
}

If you do not have to block the main channel, it will be premature to finish the main line, loop line have no chance to perform

In fact, unbuffered channel is never stored data, is only responsible for the flow of data, why do you say it?

  • Fetching data from the non-buffered channel, the data stream must be before they can come, or the current line blockage
  • Data flows unbuffered channel, if no other goroutine to take this data, then the current line blockage

So, the next you can test, in any case, we measured the size of unbuffered channel is 0 (len(channel))
if the channel is data flow, we also added data, or channel dry, we have been flowing into no data available channel to take data do? It will lead to deadlock
deadlock deadlock example:

package main

func main() {
    ch := make(chan int)

    <-ch //阻塞main goroutine,信道ch被锁
}

Implementation of this program you will see Go report this error:

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
    /Users/XXX/Go/src/main.go:6 +0x4d

Process finished with exit code 2

What is a deadlock? Operating system has talked about, all the threads or processes are waiting for the release of resources. As a program, only one goroutine, so when you add data to the inside or the data stored in it, would lock the channel, and blocks the current goroutine, that is, all of goroutine (in fact, one on the main line) are waiting for an open channel ( no one took the data channel is not open), which is slightly deadlock.
I found that the deadlock is a very interesting topic, here are a few examples of deadlock:
1. Only unbuffered channel operation in a single goroutine, be sure deadlock. For example, you only operate the channel in the main function:

package main

import "fmt"

func main() {
    ch := make(chan int)

    ch <- 1 //1流入信道,堵塞当前线, 没人取走数据信道不会打开
    fmt.Println("This line code won`t run") //在此行执行之前Go就会报死锁
}

2. The following is an example of a deadlock:

package main

import "fmt"

var ch1 = make(chan int)
var ch2 = make(chan int)

func say(s string) {
    fmt.Println(s)
    ch1 <- <- ch2 //ch1等待ch2流出的数据
}

func main() {
    go say("hello")
    <-ch1 //堵塞主线
}

Wherein the main data and the like in the outflow ch1, ch2 ch1 data such effluent, but ch2 waiting for data flows, and so are two goroutine, i.e. deadlock.

3. In fact, the summary view, why would a deadlock? Unbuffered inflow channels occurs if no flow, no flow into or out of, it leads to a deadlock. Goroutine Go understood that all or activated in unbuffered channel must be stored in a data line, a data line was taken to the job pairs. Therefore, certain deadlock following example:

package main

func main() {
    c, quit := make(chan int), make(chan int)

    go func() {
        c <- 1 //c通道的数据没有被其他goroutine读取走,堵塞当前goroutine
        quit <- 0 //quit始终没有办法写入数据
    }()
    
    <- quit //quit等待数据的写
}

Careful analysis, it is due to: wait quit main outflow channel data, quit writing waiting, and c func blocked channel, etc. are all goroutine, the deadlock.
Simple of view, a total of two lines, func line data flow into and c does not flow channel in the main line, certainly a deadlock.

However, if all is not really the case for access to the data channel is a deadlock?

Counter example is as follows:

package main

func main() {

    c := make(chan int)

    go func() {
        c <- 1
    }()
}

Program exits normally, very simple, that is not our summary does not work, or because of a people very embarrassing, main wait was not any other goroutine, their own start and finished, so no data is flowing into the channel c, the total execution a goroutine, and there is no blockage occurs, there is no deadlock error.

Then the deadlock solution?
The simplest, the removal of data is not removed, the data is not put into, since no buffer channel can not carry data, then quickly take!
Specifically, it is a deadlock situation in Example 3, so to avoid deadlock:

package main

func main() {
    c, quit := make(chan int), make(chan int)

    go func() {
        c <- 1 //c通道的数据没有被其他goroutine读取走,堵塞当前goroutine
        quit <- 0 //quit始终没有办法写入数据
    }()
    
    <- c    //取走c的数据
    <- quit //quit等待数据的写
}

Another solution is buffered channel, i.e., c is provided with a data buffer size:

c := make(chan int, 1)

In this case, c can be a data cache. That is to say, into a data, c does not suspend the current line, add a line current will hang until the first data to be removed other goroutine, which is only blocked in the capacity of a certain time, and do capacity is not blocked.

Unbuffered channel data in and out of sequence we already know, Unbuffered channel is never stored data, the inflow must outflow can.
Observe the following procedures:

package main

import "fmt"

var ch chan int = make(chan int)

func foo(id int) {
    ch <- id
}
func main() {

    //开启5个routine
    for i := 0; i < 5; i++ {
        go foo(i)
    }

    //取出信道中的数据
    for i := 0; i < 5; i++ {
        fmt.Print(<- ch)
    }
}

They opened a five goroutine, and then in turn take data. In fact, the entire implementation process of the subdivision, then the data five lines sequentially flows through the channel ch, main print it, and the macro we see that is unbuffered channel data is first come, first out, but unbuffered channel is not stored data is only responsible for the flow of data

Buffered channel

And finally to the topic, in fact, more cache channel with English speaking Tech: buffered Channel.

The meaning of the word buffer, the buffer can only flow channel data, the data can also be cached. It is the capacity, a data word is stored, you can hang on the channel, blocking the current does not have to wait for the data lines removed.

When the buffer channel reaches full state and they will show blocked, because then no longer carry more data, and "you have to take the data, the data can flow into」 .

Declare a channel, we make a second parameter to indicate to its capacity (the default is 0, i.e., no buffer):

var ch chan int = make(chan int, 2) // 写入2个元素都不会阻塞当前goroutine, 存储个数达到2的时候会阻塞

The following example, the buffer may be unbuffered channel ch inflow three elements:

package main

func main() {
    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3   
}

If you then try to flow into a data, the channel ch clog main line, reported a deadlock.
In other words, the buffer channel will be locked when at full capacity.

In fact, the channel is a FIFO buffer, we can see the queue buffer channel as a thread-safe:

package main

import "fmt"

func main() {

    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3

    fmt.Println(<-ch) //1
    fmt.Println(<-ch) //2
    fmt.Println(<-ch) //3
}

Channel data and channel close reading you may find that the above code one by one to read the channel is simply too much bother, Go range allows us to use language to read channel:

package main

import "fmt"

func main() {

    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3

    for v := range ch {
        fmt.Println(v)
    }
}

If you execute the above code, deadlock errors will be reported, because the range is not closed until the channel is not the end of the read. That is, if the buffer channel dried up, then the range will block the current goroutine, so deadlock slightly.

So, we try to avoid this situation, relatively easy to think that the read channel is empty when it ends reading:

package main

import "fmt"

func main() {

    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3

    for v := range ch {
        fmt.Println(v)
        if len(ch) <= 0 { //如果现在数据量为0,跳出循环
            break
        }
    }
}

The above method can be normally outputted, it is noted that inspection channel size method can not be used to remove all the data at the time of channel access are occurring, in this example because we only keep the data ch, it is now a one outwardly take , the channel size is decreasing.

Another way is to explicitly close the channel:

package main

import "fmt"

func main() {

    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    ch <- 3

    //显式地关闭信道
    close(ch)

    for v := range ch {
        fmt.Println(v)
    }
}

The channel is closed disables the data flows, is read-only. We can still retrieve data from a closed channel, but can not write the data.

Wait gorountine program Well, we are back to the original question, using the channel clogged main line, waiting to open out all goroutine finish.

This is a model out of a lot of small goroutine, they each ran their own, ran the final report to the main line.

We discussed the following two versions of the program:

  1. Only a single main channel congestion unbuffered
  2. A capacity of a buffer channel number goroutines

For Option 1, the code sample will probably look something like this:

package main

import "fmt"

var quit chan int //只开一个信道

func foo(id int) {
    fmt.Println(id)
    quit <- 0 //ok,finished
}

func main() {

    count := 1000
    quit = make(chan int) //无缓冲

    for i := 0; i < count; i++ {
        go foo(i)
    }

    for i := 0; i < count; i++ {
        <- quit
    }
}

For Scheme 2, into the channel 1000:

quit = make(chan int, count) // 容量1000

In fact, only difference is that a buffer is, a non-buffered.
For this scenario, the two can complete the task, are possible.

  • Unbuffered channel data is a group of one "flow in and out."
  • A channel buffer is a memory, and then flows out together

Guess you like

Origin www.cnblogs.com/itbsl/p/11456432.html