Go- concurrency and parallelism - coroutine - Channel - buffered channel read-write file -select-mutex-

Complicated by

  Go is a concurrent language, rather than a parallel language . Before discussing how to handle concurrency Go, we must understand what is concurrency, and the difference between concurrent and parallel.

What complicated by that?

  Concurrency is the ability to handle multiple tasks immediately. A case where the CPU <run simultaneously looks like means, which has a blocking state io slow waiting time only.

Example 1:

We can imagine a person is running. If his morning run when, suddenly loose shoelace. So he stopped at the Department of shoelaces, then continue to run. This is a typical example of concurrency. This person can get running and tie them about two things that immediately handle multiple tasks.

Example 2:

       The order of execution: You eat half a meal, phone, and you only have to pick up after eating, which means you do not support concurrent not support parallel.
       Concurrency: You eat half a meal, phone, and you pick up the phone stopped, then continued after a meal, it shows your support for concurrency.
       Parallel: You eat half a meal, phone, and your phone while eating side, it shows your support and

understanding:

One explanation: refers to two or more parallel events occur at the same time; and refers to two or more concurrent events occur at the same time intervals.
Explain two: Parallel multiple events on different entities, concurrent multiple events on the same entity.
Explained three: a processor on a "simultaneous" process multiple tasks simultaneously process multiple tasks across multiple processor. As hadoop distributed clusters

What parallel that? What is the difference between parallel and concurrent?

  It refers to a plurality of parallel processing tasks simultaneously. It sounds and almost concurrently, but in fact completely different.

We also use this example to help understand the running. If this man while he was jogging, still using his iPod to listen to music. Here, he is listening to music while running, which is the simultaneous processing of multiple tasks. This is called parallel.

Go support for concurrency

  Go programming language native support for concurrency. Go Go using coroutine (Goroutines) and a channel (Channel) for concurrent processing. In the next tutorial, we will detail them.

What Go coroutine that?

  Go coroutine function or method is run concurrently with other functions or methods. Go coroutines can be viewed as a lightweight thread. Compared with thread, create a Go coroutine cost very little. So Go applications often see thousands of Go coroutine to run concurrently.

Go coroutine compared to the advantages of thread

  • For comparison thread, Go coroutine very low cost. Stack size is only several kb, and may be increased or decreased depending on the application requirements. The thread must specify the size of the stack, the stack is fixed.
  • Go coroutine will re-use a smaller number of OS threads (Multiplex). Even if there are thousands of Go coroutine program, it may be only a thread. If the thread is blocking a Go Association process occurs (for example, waiting for user input), then the system will then create an OS thread, and the rest Go coroutines are moving to the new OS thread. All of this at runtime, as programmers, we are not directly confronted with these intricate details, but there is a simple API to handle concurrency.
  • Go coroutine using a channel (Channel) communication. Channel for preventing the occurrence of race conditions (Race Condition) When a plurality coroutine access to shared memory. It can be seen as a channel of communication between the pipe Go coroutine. We will discuss in detail in the next tutorial channel.

How to Start a Go coroutine?

  When you call a function or method, preceded by the keyword  go, you can make a new Go coroutine to run concurrently.

example:

package main

import (
    "fmt"
    "time"
)
// Go coroutine
func index()  {
    fmt.Println("hello world")
}
func main()  {
    go index()
    go index()
    go index()
    go index()
    fmt.Println("")
    time.Sleep(time.Second*3)

}

  Writing coroutine is added directly before the function key "go" to open coroutine, running from top to bottom, waiting for the switching process itself encountered io blocking state. For data communication between the various coroutine, a plurality of open coroutine is so simple implementation.

 Time time module of mass participation, second click to view source code:

to sum up:

  • When you start a new coroutine, coroutine call will return immediately. With different functions, program control not going to wait for Go coroutine is finished. Go after calling coroutine, program control returns immediately to the next line of code, ignoring any return value of the coroutine.
  • If you want to run another coroutine Go, Go main coroutine must continue running. If the main Go coroutine terminated, the program is terminated, then the other Go coroutine will not continue to run.

 Nobumichi (channel)

 

What is a channel?

  A communication channel between the pipe can be thought Go coroutine. As water in the pipes will flow from one end, the data may be transmitted by using the channel from one end, received at the other end.

Channel statement

  All channels are associated with a type. Only this type of transport channel data, and other types of data transport is illegal.

chan T It represents a  T type of channel . Channel type is a value to be passed as a parameter.

Zero value of the channel  nil. Zero value of the channel is no use, it should be like on the map and sliced done, with  make defined channel.

The following code is written, a declaration channel.

package main

import (
    "fmt"
    "time"
)

// channel, channel duct channel
//
main FUNC () {
 //     // channel is variable 
//     // transport channel type is an int 
//     // null reference type is nil 
//     var Chan A int 
//     fmt.Println (A) 
//     // initialize 
    var A Chan int = the make (Chan int )
    fmt.Println(a)
//     // Key: values ranging discharge 
//     // arrow to channel variation, the value is put into the channel
    a <-1
//     // outward arrow, the value indicates the channel 
    <- A
 //     // default channel **** ****** important case, the values assigned are blocked, and
//
}

Brief statement usually simple and effective way to channel a definition.

a := make(chan int)

This line also defines a channel type int a,类型可以是其他数据结构bool string int...

It is transmitted and received through the channel

  As shown below, the syntax data transmitted and received over the channel.

Data: = <- a // read channel a   
a <- Data // write channel a

Letter road direction of arrow specifies a transmission or receive data.

In the first row, for the arrow  a is outwardly pointing, we read channel  a values, and stores the value to the variable  data.

In the second line, the arrow points  a, so we write the data channel  a.

package main

import (
    "fmt"
    "time"
)
// channel, pipe, channel Channel 
FUNC main () {
     // definition of channel 
    // channel is variable 
    // int type control reference nil 
    // var Chan A int 
    // fmt.Println (A)

    // initialize 
    var A Chan int = the make (Chan int )
    fmt.Println(a)
    // Key: values ranging discharge 
    // arrow values to the channel is the channel to the variable 
    A <-1  // blocking state does not run down the wait argument 
    // arrow outwardly value indicates the channel 

    B: = < - A

    fmt.Println(b)
    the time.sleep (time.Second * 1 )
     // channel default ****** **** important case, and the value of the assignment is blocked 
}

The default channel, and the value assignment is blocked, and the deposit need to be taken between the passed parameter of the communication channel through, or will cause deadlock.

Send and receive default is blocked

  Send and receive default is blocked. What does it mean? When the data is transmitted to the channel, the program control will occur at the statement transmission data block until some other coroutine Go to read data from the channel, will be unblocked. Similarly, when reading the data channel, if no other coroutine write data into this channel, then the read process will always blocked.

Such characteristics of the channel can support efficient communication between Go coroutine, no need to use an explicit condition variables locks or other common programming languages.

Sample code channels

  Next, to write the code to see through a channel of communication between a coroutine is how it.

package main

import (
    "fmt"
    "time"
)
// communication transfer between the go channel coroutine 
func index1 (a chan bool) {
    fmt.Println("hello go")
    // 一旦执行完毕,往信道中存放一个值
    time.Sleep(time.Second*3)
    a<-true
    fmt.Println("Just do IT")
}
func main()  {
    // 必须初始化
    var a chan bool = make(chan bool)
    go index1(a)
    // 从信道中取值
    <-a
    //b:=<-a
    //fmt.Println(b)
    time.Sleep(time.Second*3)
}

加入time.sleep模拟IO阻塞态,来更好的理解go协程中使用信道通信的传递。

 

 信道的代码示例:

  我们再编写一个程序来更好地理解信道。该程序会计算一个数中每一位的平方和与立方和,然后把平方和与立方和相加并打印出来。

例如,如果输出是 123,该程序会如下计算输出:

squares = (1 * 1) + (2 * 2) + (3 * 3) 
cubes = (1 * 1 * 1) + (2 * 2 * 2) + (3 * 3 * 3) 
output = squares + cubes = 50

 

我们会这样去构建程序:在一个单独的 Go 协程计算平方和,而在另一个协程计算立方和,最后在 Go 主协程把平方和与立方和相加。

package main

import "fmt"

// 例子

//number =456
func calcSquares(number int, squareop chan int) {
    sum := 0
    for number != 0 {
        digit := number % 10
        sum += digit * digit
        number /= 10
    }
    squareop <- sum
}
func calcCubes(number int, a chan int) {
    sum := 0
    for number != 0 {
        digit := number % 10
        sum += digit * digit*digit
        number /= 10
    }
    a <- sum
}
func main() {
    //运输平方和
    var a chan int=make(chan int)
    //运输立方和
    var b chan int=make(chan int)
    number:=123
//开启两个go协程 go calcSquares(number,a) go calcCubes(number,b) sum1:=<-a sum2:=<-b // 开两个协程分别进行,最后计算总和即可,速度很快 fmt.Println(sum1+sum2) }

协程分任务执行,速度很快。

 

 

 死锁

  使用信道需要考虑的一个重要的是死锁,当GO协程给一个信道发送数据时,照理说会有其他G协程来接收数据,

如果没有的话,程序会在运行时触发panic,形成死锁现象。

同理,当有 Go 协程等着从一个信道接收数据时,我们期望其他的 Go 协程会向该信道写入数据,要不然程序就会触发 panic。

package main

func main() {  
    ch := make(chan int)
    ch <- 5
}

没有接收信道的值会报错:

单向信道

  我们目前讨论的信道都是双向信道,即通过信道既能发送数据,又能接收数据。其实也可以创建单向信道,这种信道只能发送或者接收数据。

package main

import "fmt"
//单向信道 只能放或者只能取
func sendData(sendch chan<- int) {
    sendch <- 10

}
func main() {
    // 只写信道
    sendch := make(chan int)
    go sendData(sendch)
    fmt.Println(<-sendch)
}

...>>> 10

关闭信道和使用 for range 遍历信道

  数据发送方可以关闭信道,通知接收方这个信道不再有数据发送过来

  当从信道接收数据时,接收方可以多用一个变量来检查信道是否已经关闭。

v, ok := <- ch

  上面的语句里,如果成功接收信道所发送的数据,那么 ok 等于 true。而如果 ok 等于 false,说明我们试图读取一个关闭的通道。从关闭的信道读取到的值会是该信道类型的零值。例如,当信道是一个 int 类型的信道时,那么从关闭的信道读取的值将会是 0

package main

import "fmt"

//关闭信道和使用 for range 遍历信道

func producer(chnl chan int) {
    for i := 0; i < 10; i++ {
        chnl <- i
    }
    close(chnl)
}
func main() {
    ch := make(chan int)
    go producer(ch)
    for {
        v, ok := <-ch

        // 如果信道关闭,ok就是false
        //如果没关闭,就是true
        if ok == false {
            break
        }
        fmt.Println("Received ", v, ok)
    }
}

   主函数用的是for循环,会打印出0-9,然后关闭通道,使用变量 ok,检查信道是否已经关闭。如果 ok 等于 false,说明信道已经关闭,于是退出 for 循环。如果 ok 等于 true,会打印出接收到的值和 ok 的值。

 用range来循环读取数据,range会自动判断,取完值后直接关闭,比for更好一些

package main

import (  
    "fmt"
)

func producer(chnl chan int) {  
    for i := 0; i < 10; i++ {
        chnl <- i
    }
    close(chnl)
}
func main() {  
    ch := make(chan int)
    go producer(ch)
    for v := range ch {
        fmt.Println("Received ",v)
    }
}

for range 循环从信道 ch 接收数据,直到该信道关闭。一旦关闭了 ch,循环会自动结束。该程序会输出:

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

用for-range优化上述计算平和的例子:

package main

import "fmt"

//优化例子
//number=456
func digits(number int, dchnl chan int) {
for number != 0 {
digit := number % 10
dchnl <- digit
number /= 10
}
close(dchnl)
}
func calcSquares1(number int, squareop chan int) {
sum := 0
dch := make(chan int)
go digits(number, dch)
for digit := range dch {
sum += digit * digit
}
squareop <- sum
}

func calcCubes1(number int, cubeop chan int) {
sum := 0
dch := make(chan int)
go digits(number, dch)
for digit := range dch {
sum += digit * digit * digit
}
cubeop <- sum
}

func main() {
number := 123
sqrch := make(chan int)
cubech := make(chan int)
go calcSquares1(number, sqrch)
go calcCubes1(number, cubech)
squares:= <-sqrch
cubes :=<-cubech
fmt.Println("Final output", squares+cubes)
}

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

  

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  

Guess you like

Origin www.cnblogs.com/Gaimo/p/12037018.html