并行计算

1、Goroutine协程:协程就是go提供的轻量级的独立运算过程,比线程还轻;创建一个协程,就是使用go关键字,后面跟上要运行的函数

案例要求:1)、先计算1000以下所有能够被3整除的整数的和A

     2)、然后计算1000以下所有能够被5整除的整数和B

     3)、然后再计算1000以下所有能够被3和5整除的整数和C

     4)、使用A+B-C得到最后结果

案例中,使用go关键字创建一个协程,协程函数内使用channel(chan数据,类似队列,在取chan中数据时,如果为空,会阻塞,直到chan中有数据)存储数据

package main

import (
    "fmt"
    "runtime"
    "time"
)

func get_sum_of_divisible(num int, divider int, resultChan chan int) {
    sum := 0
    for value := 0; value < num; value++ {
        if value%divider == 0 {
            sum += value
        }
    }
    resultChan <- sum
}

func main() {
    runtime.GOMAXPROCS(runtime.NumCPU())
    LIMIT := 1000
    //用于被15除结果
    job1 := make(chan int, 1)
    //用于被3,5除结果
    job2 := make(chan int, 2)

    t_start := time.Now()
    go get_sum_of_divisible(LIMIT, 15, job1)
    go get_sum_of_divisible(LIMIT, 3, job2)
    go get_sum_of_divisible(LIMIT, 5, job2)

    sum15 := <-job1
    sum3, sum5 := <-job2, <-job2

    sum := sum3 + sum5 - sum15
    t_end := time.Now()
    fmt.Println(sum)
    fmt.Println(t_end.Sub(t_start))
}

2、Channel通道:提供了协程之间的通信方式以及运行同步机制

案例:假设训练定点投篮和三分投篮,教练在计数

如果向channel里面写信息,必须有配对的取信息的一端;假如把下面的go count(c)注释掉,则不会有打印数据

package main

import (
    "fmt"
    "strconv"
    "time"
)

func fixed_shooting(msg_chan chan string) {
    i := 1
    for {
        msg_chan <- "fixed shooting"
        var str string = strconv.Itoa(i)
        fmt.Println("continue fixed shooting..." + str)
        i++
    }
}

func count(msg_chan chan string) {
    for {
        msg := <-msg_chan
        fmt.Println(msg)
        time.Sleep(time.Second * 1)
    }
}

func main() {
    var c chan string
    c = make(chan string)

    go fixed_shooting(c)
    go count(c)

    var input string
    fmt.Scanln(&input)
}

加一个三分投篮方法,可以看出,写入channel中的数据,必须读取出来才能再次写入,如果没有读取出来,再次写入会失败

package main

import (
    "fmt"
    "strconv"
    "time"
)

func three_point_shooting(msg_chan chan string) {
    i := 1
    for {
        var str string = strconv.Itoa(i)
        msg_chan <- "three point shooting--" + str
        i++
    }
}

func fixed_shooting(msg_chan chan string) {
    i := 1
    for {
        var str string = strconv.Itoa(i)
        msg_chan <- "fixed shooting--" + str
        i++
    }
}

func count(msg_chan chan string) {
    for {
        msg := <-msg_chan
        fmt.Println(msg)
        time.Sleep(time.Second * 1)
    }
}

func main() {
    var c chan string
    c = make(chan string)

    go fixed_shooting(c)
    go three_point_shooting(c)
    go count(c)

    var input string
    fmt.Scanln(&input)
}

结果图如下,结果是交替输出的,也就是channel中输入的必须输出后才能再次输入

3、Channel通道方向

var c chan string  可读写channel,可写入也可读取

var c chan<- string  只写channel,只能写入

var c <-chan string  只读channel,只能读取

4、多通道(Select)

案例中,select依次检查每个channel是否有消息传递过来,如果有就输出;如果同时有多个消息到达,select随机选择一个channel来从中读取消息;如果没有一个channel有消息到达,那么select语句就阻塞在这里一直等待。select 语句也可以有default片段,case不符合时会执行default语句片段。select中case、default类似switch中case、default

下面case中使用了time.After,可以这样理解,main函数也是一个channel,time.After让main阻塞指定时间后,读出时间消息(案例中没有用变量存储返回的时间)

package main

import (
    "fmt"
    "time"
)

func fixed_shooting(msg_chan chan string) {
    var times = 3
    var t = 1
    for {
        if t <= times {
            msg_chan <- "fixed shooting"
        }
        t++
        time.Sleep(time.Second * 1)
    }
}

func three_point_shooting(msg_chan chan string) {
    var times = 3
    var t = 1
    for {
        if t <= times {
            msg_chan <- "three point shooting"
        }
        t++
        time.Sleep(time.Second * 1)
    }
}

func main() {
    c_fixed := make(chan string)
    c_3_point := make(chan string)

    go fixed_shooting(c_fixed)
    go three_point_shooting(c_3_point)

    go func() {
        for {
            select {
            case msg1 := <-c_fixed:
                fmt.Println(msg1)
            case msg2 := <-c_3_point:
                fmt.Println(msg2)
            case <-time.After(time.Second * 5):
                fmt.Println("timeout check again...")
            }
        }
    }()

    var input string
    fmt.Scanln(&input)
}

结果图

5、Channel Buffer通道缓冲区:一般我们定义的channel都是同步的,也就是说接受端和发送端彼此等待对方OK才开始。但是如果执行了channel缓冲区大小,那么消息的发送和接收是异步的,除非channel缓冲区已经满了

package main

import (
    "fmt"
    "strconv"
    "time"
)

func shooting(msg_chan chan string) {
    var group = 1
    for {
        for i := 1; i <= 10; i++ {
            msg_chan <- strconv.Itoa(group) + ":" + strconv.Itoa(i)
        }
        group++
        time.Sleep(time.Second * 5)
    }
}

func count(msg_chan chan string) {
    for {
        fmt.Println(<-msg_chan)
    }
}

func main() {
    var c = make(chan string, 20)
    go shooting(c)
    go count(c)
    var input string
    fmt.Scanln(&input)
}
原文地址:https://www.cnblogs.com/hujiapeng/p/9651701.html