设为首页 加入收藏

TOP

go语言之并发(三)
2017-09-30 13:56:59 】 浏览:9144
Tags:语言 并发
获取或改变其状态,那又该如何做呢。可以将这个变量至于0通道中,并使用一个协程来维护。

Go语言并发之美

下面的例子描述如何用这个方式,实现一个共享变量。

 

//共享变量有一个读通道和一个写通道组成
typesharded_var struct {
         reader chan int
         writer chan int
}
//共享变量维护协程
funcsharded_var_whachdog(v sharded_var) {
         go func() {
                   //初始值
                   var value int = 0
                   for {
                            //监听读写通道,完成服务
                            select {
                            case value =<-v.writer:
                            case v.reader <-value:
                            }
                   }
         }()
}
funcmain() {
         //初始化,并开始维护协程
         v := sharded_var{make(chan int),make(chan int)}
         sharded_var_whachdog(v)
         //读取初始值
         fmt.Println(<-v.reader)
         //写入一个值
         v.writer <- 1
         //读取新写入的值
         fmt.Println(<-v.reader)
}

 

这样,就可以在协程和通道的基础上实现一个协程安全的共享变量了。定义一个写通道,需要更新变量的时候,往里写新的值。再定义一个读通道,需要读的时候,从里面读。通过一个单独的协程来维护这两个通道。保证数据的一致性。

        一般来说,协程之间不推荐使用共享变量来交互,但是按照这个办法,在一些场合,使用共享变量也是可取的。很多平台上有较为原生的共享变量支持,到底用那种 实现比较好,就见仁见智了。另外利用协程和通道,可以还实现各种常见的并发数据结构,如锁等等,就不一一赘述。

  7.协程泄漏

        协程和内存一样,是系统的资源。对于内存,有自动垃圾回收。但是对于协程,没有相应的回收机制。会不会若干年后,协程普及了,协程泄漏和内存泄漏一样成为 程序员永远的痛呢?一般而言,协程执行结束后就会销毁。协程也会占用内存,如果发生协程泄漏,影响和内存泄漏一样严重。轻则拖慢程序,重则压垮机器。

        C和C++都是没有自动内存回收的程序设计语言,但只要有良好的编程习惯,就能解决规避问题。对于协程是一样的,只要有好习惯就可以了。

        只有两种情况会导致协程无法结束。一种情况是协程想从一个通道读数据,但无人往这个通道写入数据,或许这个通道已经被遗忘了。还有一种情况是程想往一个通道写数据,可是由于无人监听这个通道,该协程将永远无法向下执行。下面分别讨论如何避免这两种情况。

        对于协程想从一个通道读数据,但无人往这个通道写入数据这种情况。解决的办法很简单,加入超时机制。对于有不确定会不会返回的情况,必须加入超时,避免出 现永久等待。另外不一定要使用定时器才能终止协程。也可以对外暴露一个退出提醒通道。任何其他协程都可以通过该通道来提醒这个协程终止。
Go语言并发之美
对于协程想往一个通道写数据,但通道阻塞无法写入这种情况。解决的办法也很简单,就是给通道加缓冲。但前提是这个通道只会接收到固定数目的写入。比方说, 已知一个通道最多只会接收N次数据,那么就将这个通道的缓冲设置为N。那么该通道将永远不会堵塞,协程自然也不会泄漏。也可以将其缓冲设置为无限,不过这 样就要承担内存泄漏的风险了。等协程执行完毕后,这部分通道内存将会失去引用,会被自动垃圾回收掉。
funcnever_leak(ch chan int) {
         //初始化timeout,缓冲为1
         timeout := make(chan bool, 1)
         //启动timeout协程,由于缓存为1,不可能泄露
         go func() {
                   time.Sleep(1 * time.Second)
                   timeout <- true
         }()
         //监听通道,由于设有超时,不可能泄露
         select {
         case <-ch:
                   // a read from ch hasoccurred
         case <-timeout:
                   // the read from ch has timedout
         }
}

 

        上面是个避免泄漏例子。使用超时避免读堵塞,使用缓冲避免写堵塞。

        和内存里面的对象一样,对于长期存在的协程,我们不用担心泄漏问题。一是长期存在,二是数量较少。要警惕的只有那些被临时创建的协程,这些协程数量大且生 命周期短,往往是在循环中创建的,要应用前面提到的办法,避免泄漏发生。协程也是把双刃剑,如果出问题,不但没能提高程序性能,反而会让程序崩溃。但就像 内存一样,同样有泄漏的风险,但越用越溜了。
 
并发模式之实现

        在并发编程大行其道的今天,对协程和通道的支持成为各个平台比不可少的一部分。虽然各家有各家的叫法,但都能满足协程的基本要求—并发执行和可大量创建。笔者对他们的实现方式总结了一下。

        下面列举一些已经支持协程的常见的语言和平台。
Go语言并发之美
GoLang 和Scala作为最新的语言,一出生就有完善的基于协程并发功能。Erlang最为老资格的并发编程语言,返老还童。其他二线语言则几乎全部在新的版本中加入了协程。

        令人惊奇的是C/C++和Java这三个世界上最主流的平台没有在对协程提供语言级别的原生支持。他们都背负着厚重的历史,无法改变,也无需改变。但他们还有其他的办法使用协程。

        Java平台有很多方法实现协程:

        · 修改虚拟机:对JVM打补丁来实现协程,这样的实现效果好,但是失去了跨平台的好处

        · 修改字节码:在编译完成后增强字节码,或者使用新的JVM语言。稍稍增加了编译的难度。

        · 使用JNI:在Jar包中使用JNI,这样易于使用,但是不能跨平台。

        · 使用线程模拟协程:使协程重量级,完全依赖JVM的线程实现。

        其中修改字节码的方式比较常见。因为这样的实现办法,可以平衡性能和移植性。最具代表性的JVM语言Scale就能很好的支持协程并发。流行的Java Actor模型类库akka也是用修改字节码的方式实现的协程。

        对于C语言,协程和线程一样。可以使用各

首页 上一页 1 2 3 4 下一页 尾页 3/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇golang 标准库间依赖的可视化展示 下一篇Golang控制goroutine的启动与关闭

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目