go语言学习(七)

时间:2022-12-12 19:59:08

1.go语言并发优势

2.并发小程序

3.runtime包使用

4.channel使用

一、go语言并发优势

并发就是单核cpu通过时间片轮转,“同时”处理多个任务。并发是现代程序设计非常重要的一个环节,而go语言在语言层面支持高并发,一个普通的计算机就能支持上万个goroutine竞争资源。同时代码实现简单,开发效率高。

二、go语言并发demo

package main
 
import "fmt"
import "time"
 
func sayHello(id int) {
 
    //每隔两秒打印一次
    for i := 0; i < 5; i++ {
        fmt.Println("Hello ", id)
        time.Sleep(time.Second * 2)
    }
}
 
func main() {
    for i := 0; i < 10; i++ {
        //开启一个goroutine,每个两秒打印一个数字
        go sayHello(i)
    }
 
    //使主goroutine不会退出
    for {
    }
}

在没有使用并发时,程序会一句一句的打印,使用并发之后程序会十个十个的打印五次。最后的for循环为了使主协程不会在执行完毕后推出,否则主协退出子协程没来得及打印将不会看到效果。

三、runtime包使用

runtime.Gosched(),让出时间片。

package main
 
import "fmt"
import "runtime"
 
func newTask() {
    for i := 0; i < 5; i++ {
        fmt.Println("go")
    }
}
 
func main() {
 
    go newTask()
    //正常情况下主协程执行完毕之后,子协程未来得及调用,调用runtime.Gosched()方法后,主协程让出时间片,子协程先执行完
    runtime.Gosched()
    fmt.Println("hello")
}

打印结果为:

go
go
go
go
go
hello

runtime.Goexit(),终止协程。

在上面代码中加入runtime.Goexit(),使子协程打印三次之后中断。

package main
 
import "fmt"
import "runtime"
 
func newTask() {
    for i := 0; i < 5; i++ {
        fmt.Println("go")
        if i == 2 {
            runtime.Goexit()
        }
    }
}
 
func main() {
 
    go newTask()
    //正常情况下主协程执行完毕之后,子协程未来得及调用,调用runtime.Gosched()方法后,主协程让出时间片,子协程先执行完
    runtime.Gosched()
    fmt.Println("hello")
}

打印结果为:

go
go
go

hello

runtime.GOMAXPROCS()方法可以设置使用cpu核数,默认情况下cpu所有核都在工作。

package main
 
import "fmt"
import "runtime"
 
func main() {
 
    //设置工作cpu两核
    n := runtime.GOMAXPROCS(2)
    fmt.Print(n)
 
    for {
        go fmt.Print(1)
        fmt.Print(0)
    }
}

运行结果可以看出,cpu核数越少,0和1将会大面积打印。

四、channel使用

1.使用channel实现同步。子协程执行完毕后主协程再结束。

package main
 
import "fmt"
 
func main() {
 
    //定义一个channel
    ch := make(chan string)
 
    defer fmt.Println("主协程执行完毕!!!!")
 
    go func() {
 
        defer fmt.Println("子协程执行完毕!!!!")


        for i := 0; i < 5; i++ {
            fmt.Println("Hello i = ", i)
        }
 
        ch <- "Hello World"
    }()
 
    str := <-ch
    fmt.Println("子协程发来消息:", str)
}

2.无缓冲的channel只要数据未被取出程序就会阻塞,有缓冲的channel在缓冲放满之后程序阻塞。

使用range遍历channel;

package main
 
import "fmt"
 
func main() {
 
    //定义一个channel
    ch := make(chan string, 2)
 
    go func() {
 
        for i := 0; i < 5; i++ {
            fmt.Println("Hello i = ", i)
            ch <- "Hello"
        }
        //不需要读写是关闭channel
        close(ch)
    }()
 
    for str := range ch {
        fmt.Println("子协程发来消息:", str)
    }
}

代码中设置了channel的缓冲为2,当没有缓冲时,程序打印结果;

Hello i =  0
Hello i =  1
子协程发来消息: Hello
子协程发来消息: Hello
Hello i =  2
Hello i =  3
子协程发来消息: Hello
子协程发来消息: Hello
Hello i =  4
子协程发来消息: Hello

加入缓冲后,程序打印结果:

Hello i =  0
Hello i =  1
Hello i =  2
Hello i =  3
子协程发来消息: Hello
子协程发来消息: Hello
子协程发来消息: Hello
子协程发来消息: Hello
Hello i =  4

子协程发来消息: Hello

3.单向channel使用

package main
 
import "fmt"
 
//只负责向channel中放数据
func producter(out chan<- int) {
    for i := 0; i < 10; i++ {
        out <- i
    }
 
    close(out)
}
 
//只负责从channel中取数据
func customer(in <-chan int) {
    for data := range in {
        fmt.Println("Hello :", data)
    }
}
 
func main() {
    ch := make(chan int)
 
    go producter(ch)
 
    customer(ch)
}
使用单向channel实现生产者消费者模式。