go语言的并发

4 篇文章 0 订阅
4 篇文章 0 订阅

go语言的并发

并发模式

根据之前的内容,可以写一个简单的生成器,生成channel,不断发送数据。
使用生成器生成多个channel,从channel中接收数据,就是简单的并发

//生成器
func createChan()chan int{
    out := make(chan int)
    go func(){
        for{
            time.Sleep(time.Second)
            out <- 0
        }
    }()
    return out
}

//main函数中创建两个channel,接收数据
func main(){
    c1,c2 := createChan(),createChan()
    for {
        fmt.Println(<-c1)
        fmt.Println(<-c2)
    }
}

上述就是一个简单的并发模式,但不是正经的并发。因为每次都要先从c1接收数据,再从c2接收数据,某一个channel没接收到还要等着,才能接收另一个channel

要实现真正的并发,可以将从多个channel中接收的数据,只要接收到就发送给新的一个channel,main函数中只从这一个channel中接收数据。

多个channel的数据发送给一个channel有两种实现方式,一种是为每一个channel创建一个goroutine,这样就不会互相影响了,另一种方式是使用select调度器,哪个channel先有数据,就接收它,只用开一个goroutine。

两种方式各有优势,适用情况不同

方式一

适用情况

当channel个数不确定时使用,参数设为可变参数,然后在函数内遍历

简单形式
func processChan(c1,c2 chan int) chan int{
    out := make(chan int)
    go func(){
        for{
            out <- <-c1
        }
    }()
    go func(){
        for{
            out <- <-c1
        }
    }()
    return out
}
完全体
//这里...代表可变参数,一个或多个chan int
func processChan(ch ...chan int) chan int{
    out := make(chan int)
    for c := range ch{
        //这里传递了参数,而不是直接使用外部的变量
        //之前提过开goroutine尽量不要直接使用外部变量
        //这里如果直接使用外部的c,结果会只从最后一个channel中接收数据
        //因为开了goroutine并不会立即执行,而是当接收到数据后才会向out发送
        //而c只有一个,循环一遍开了多个goroutine,这是c就已经是最后一个channel了,所有的goroutine再使用c的话就全是最后一个channel了
        //所以c作为参数传递给goroutine,因为是值传递,每个goroutine使用的都是对应的channel了
        go func(c chan int){
            for{
                out <- <-c1
            }
        }(c)
    }
    return out
}

方式二

使用情况

当channel的个数确定时,优点在于只需要开一个goroutine

例子
func processChanWithSelect(c1,c2 chan int) chan int{
    out := make(chan int)
    for {
        select{
            case n := <- c1:
                out <- n
            case n := <- c2:
                out <- n
        }
    }
    return out
}

并发机制

根据之前的知识,我们可以实现一些并发机制,如:非阻塞机制、超时机制、中断退出、优雅退出

非阻塞机制

传入一个channel,如果接收到数据,就返回接收到的数据以及true,如果没有接收到数据,则返回自定义数据以及false

func noBlock(c chan int) (int,bool){
    select{
        case n := <- c:
            return n,true
        default:
            return -1,false
    }
}
//这样在接收数据时可以使用该函数,然后判断,如果为false则跳过接收数据

超时机制

使用time.Tick来实现,每次等待一段时间,当收到了time.Tick上的channel传来的数据,则返回timeout提示

func isTimeOut(c chan int)(int,bool){
    select{
        case n := <- c:
            return n,true
        case <- time.Tick(time.Second):
            fmt.Println("time out")
            return -1,false
    }
}

中断退出

之前创建的生成器,生成的是不断发送数据的channel,在main函数中不断接收数据。这种情况下,没有接收完数据,程序一般不会退出。

但是当我们创建一个生成器,生成不断接收数据的channel,在main函数中不断发送数据,channel接收到数据后输出。这样就会有一个问题,有可能main函数发送完数据,没等到从channel中接收到数据并输出,就退出了程序,这样生成器中正在执行的内容就会突然中断。

为了避免这个问题,可以向生成器中传入一个channel,当main函数中发送完数据后,向这个channel中发送个数据,生成器从这个channel中接收到数据就不再继续之前内容,开始执行退出过程

//使用chan struct{}是因为如果不传数据,不占空间
func createChan(done chan struct{})chan int{
    out := make(chan int)
    go func(d chan struct{}){
        for{
            select {
                //每隔1秒从out取一次数据,在这段间隔内就会一直判断done是否有数据
                case <- time.Tick(time.Second):
                    fmt.Println(<-out)
                case <- done:
                    fmt.Println("good bye")
                    return
            }
        }
    }(done)
    return out
}

优雅退出

main函数中向生成器生成的channel中发送完数据后,有可能直接退出,也有可能继续执行其他的功能模块,并没有判断生成器中的goroutine是否执行完退出,所以要实现优雅的退出,可以在生成器的goroutine中执行完数据退出的过程中向done再发送个数据,然后在main函数中接收数据。

这样如果goroutine没执行完,没有向done中发送数据,main函数中就会一直等待着,到接收到为止

//使用chan struct{}是因为如果不传数据,不占空间
func createChan(done chan struct{})chan int{
    out := make(chan int)
    go func(d chan struct{}){
        for{
            select {
                //每隔1秒从out取一次数据,在这段间隔内就会一直判断done是否有数据
                case <- time.Tick(time.Second):
                    fmt.Println(<-out)
                case <- done:
                    fmt.Println("cleaning up")
                    fmt.Println("cleaned yes")
                    fmt.Println("good bye")
                    done <- 1
                    return
            }
        }
    }(done)
    return out
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值