Go 多线程 (3) channel 通道

通道

atomic和互斥锁都可以保证线程安全, 不过在go语言里, 还有一种有趣的实现就是通道

package main

 

import (

    "fmt"

)

 

func main() {

    buffered := make(chan string, 10)

    buffered <- "hello world"

    value := <-buffered

    fmt.Println(value)

}

当一个资源需要在goroutine之间共享时, 通道在goroutine之间传递数据, 并且还提供了同步交换数据的机制.

在go语言中, 你需要使用 内置函数 make 来创建通道, 创建时需要指定数据类型

无缓冲的整数型通道

unbuffered := make ( chan int )

有缓冲的字符串通道

buffered := make ( chan string , 10 )

无缓冲通道

unbuffered channel 是指在接收前没有能力保存任何值的通道. 这种类型的通道要求发送方(goroutine) 和接收方(goroutine)同时准备好, 才能完成发送和接收操作.
如果两个goroutine没有同时准备好, 通道会导致先执行发送或者接收的goroutine阻塞等待. 这种对通道进行发送和接收的交互行为本身就是同步的.

其中任意一个操作都无法离开另一方操作独立存在

之前的例子里我们写过一个线程不安全的版本

package main

 
import (

    "fmt"

    "runtime"

    "sync"

)

 
var (

    count int32

    wg sync.WaitGroup

)

 
func main() {

    wg.Add(2)

    go addValue()

    go addValue()

    wg.Wait()

    fmt.Println(count)

}

 
func addValue() {

    defer wg.Done()

    for i := 0; i < 2; i++ {

        value := count

        runtime.Gosched()

        value++

        count = value

    }

}

上面这段代码使用channel的线程安全版本是

package main

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

var (
    count int32
    channel chan int32
    wg sync.WaitGroup
)

func main() {
    runtime.GOMAXPROCS(2)
    channel = make(chan int32, 10)
    count = 0
    channel <- count
    wg.Add(2)
    go addValue()
    go addValue()
    wg.Wait()
    count = <-channel
    fmt.Println(count)
}

func addValue() {
    defer wg.Done()
    for i := 0; i < 200; i++ {
        value := <-channel
        runtime.Gosched()
        value++
        channel <- value
    }
}