Go并发编程——channel

以前从未接触过并发编程,所以,且写且学吧

引入channel

package main

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

var counter int = 0

func Count(lock *sync.Mutex){
    lock.Lock()
    counter++
    fmt.Println(counter)
    lock.Unlock()
}

func main(){
    lock := &sync.Mutex{}
    for i:=0;i<100;i++{
        go Count(lock)
    }

    for{
        lock.Lock()

        c:=counter

        lock.Unlock()

        runtime.Gosched()
        if c>=100{
            break
        }
    }
}

将counter从0加至10

但是上面的做法太过复杂,一直对lock进行引用,所以引用channel


package main

import "fmt"

func Count(ch chan int){
    ch <- 1
    fmt.Println("Counting")
}

func main(){
    chs := make([]chan int, 10)
    for i:=0;i<10;i++{
        chs[i]=make(chan int)
        go Count(chs[i])
    }

    for _,ch := range(chs){
        <-ch
    }

    fmt.Println(chs)
}

简单的解释是,我们通过ch <- 1语句向对应的channel中写入一个数据。在这个channel被读取前,这个操作是阻塞的。

在所有的goroutine启动完成后,我们通过<-ch语句从10个channel中依次读取数据。在对应的channel写入数据前,这个操作也是阻塞的。

从而使用这个操作替代了锁的使用。


下面对channel进行详细介绍:

1.基本语法:

声明一个channel

var ch chan int

var m map[string] chan bool

定义一个channel

s := make(chan int)

在channel的用法中,最常见的包括写入和读出。将一个数据写入(发送)至channel的语法很直观,如下:

ch <- value

向channel写入数据通常会导致程序阻塞,直到有其他goroutine从这个channel中读取数据。从channel中读取数据的语法是

value := <-ch

如果channel之前没有写入数据,那么从channel中读取数据也会导致程序阻塞,直到channel中被写入数据为止。

2.select:

select 用于监控一系列文件文件句柄,一旦其中一个文件句柄发生了IO操作,该select()调用就会被返回

//这里注意,一定要定义channel之后才能使用
ch := make(chan int, 1)
for {
select {
case ch <- 0:
case ch <- 1:
}
i := <-ch
fmt.Println("Value received:", i)
}

3.缓冲机制:

c := make(chan int, 1024)

这样子即使没有读取方,可以一直向channel中写入,直至缓冲区被填满

可以使用range关键来实现更为简便的循环读取:

这里不知道为什么总是会出错!!!!

for i := range c{
fmt.Println(i)
}

总体程序

     c := make(chan int, 10)

     for i:=0;i<10;i++{
	      c<-12
       }

     fmt.Println(len(c))
    
     for j := range c{
         fmt.Println(j)
       }

4.超时机制:

在并发编程的通信过程中,最需要处理的就是超时问题,即向channel写数据时发现channel已满,

或者从channel试图读取数据时发现channel为空。

如果不正确处理这些情况,很可能会导致整个goroutine锁死.即deadlock发生!

i:= <-ch

这个语句在ch中有值写入时,没有问题,一旦ch中为空,没有值写入,会出现死锁。

故而,超时是一个非常实际的方法,在Go语言中,主要是借用select语句进行模拟超时

timeout := make(chan bool,1)
ch :=make(chan int)

go func(){
time.sleep(1)
timeout<-true
}()

select{
case <-ch:
case <-timeout:
}

5.单向channel:

单向channel变量的声明

var ch1 chan int
// ch1是一个正常的channel,不是单向的
var ch2 chan<- float64// ch2是单向channel,只用于写float64数据
var ch3 <-chan int
// ch3是单向channel,只用于读取int数据

单向channel的初始化

ch4 := make(chan int)
ch5 := <-chan int(ch4) // ch5就是一个单向的读取channel
ch6 := chan<- int(ch4) // ch6 是一个单向的写入channel

使用方法

func Parse(ch <-chan int) {
for value := range ch {
fmt.Println("Parsing value", value)
}
}

6.关闭channel

关闭channel非常简单,直接使用Go语言内置的close()函数即可:
close(ch)

判断一个channel是否已经被关闭,可以在读取的时候使用多重返回值的方式:
x, ok := <-ch
第二个bool返回值是false则表示ch已经被关闭





本文来自:CSDN博客

感谢作者:luan_tianjiao

查看原文:Go并发编程——channel

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。