zoukankan      html  css  js  c++  java
  • goroutine

      在Go语言中,每一个并发的执行单元叫作一个goroutine;

    当一个程序启动时,其主函数即在一个单独的goroutine中运行,我们叫它main goroutine。新的goroutine会用go语句来创建。在语法上,go语句是一个普通的函数或方法调用前加上关键字go。go语句会使其语句中的函数在一个新创建的goroutine中运行。而go语句本身会迅速地完成。

    f()    // call f(); wait for it to return
    go f() // create a new goroutine that calls f(); don't wait

    是不是有点像脚本语言里面的&作用

    package main
    
    import "fmt"
    import "time"
    
    
    func spinner(delay time.Duration) {
        for {
            for _, r := range `-|/` {
                fmt.Printf("
    %c", r)
                time.Sleep(delay)
            }
        }
    }
    
    func fib(x int) int {
        if x < 2 {
            return x
        }
        return fib(x-1) + fib(x-2)
    }
    
    func main() {
        go spinner(1 * time.Millisecond)
        const n = 30
        fibN := fib(n) // slow
        fmt.Printf("
    Fibonacci(%d) = %d
    ", n, fibN)
    }
    
    结果为:
    -
    
    |
    /
    -
    
    Fibonacci(30) = 832040
    View Code

    Channels

      一个channel是一个通信机制,它可以让一个goroutine通过它给另一个goroutine发送值信息。每个channel都有一个特殊的类型,也就是channels可发送数据的类型。一个可以发送int类型数据的channel一般写为chan in

      使用内置的make函数,我们可以创建一个channel:

    ch := make(chan int) // ch has type 'chan int'

      和map类似,channel也对应一个make创建的底层数据结构的引用。当我们复制一个channel或用于函数参数传递时,我们只是拷贝了一个channel引用,因此调用者和被调用者将引用同一个channel对象。和其它的引用类型一样,channel的零值也是nil。

    两个相同类型的channel可以使用==运算符比较。如果两个channel引用的是相同的对象,那么比较的结果为真。一个channel也可以和nil进行比较。

      一个channel有发送和接受两个主要操作,都是通信行为。一个发送语句将一个值从一个goroutine通过channel发送到另一个执行接收操作的goroutine。发送和接收两个操作都使用<-运算符。在发送语句中,<-运算符分割channel和要发送的值。在接收语句中,<-运算符写在channel对象之前。一个不使用接收结果的接收操作也是合法的。

    ch <- x  // a send statement
    x = <-ch // a receive expression in an assignment statement
    <-ch     // a receive statement; result is discarded

      Channel支持close操作,用于关闭channel,随后对基于该channel的任何发送操作都将导致panic异常。

    对一个已经被close过的channel进行接收操作依然可以接受到之前已经成功发送的数据;如果channel中已经没有数据的话将产生一个零值的数据。

    不带缓存的Channels

      一个基于无缓存Channels的发送操作将导致发送者goroutine阻塞,直到另一个goroutine在相同的Channels上执行接收操作,当发送的值通过Channels成功传输之后,两个goroutine可以继续执行后面的语句。反之,如果接收操作先发生,那么接收者goroutine也将阻塞,直到有另一个goroutine在相同的Channels上执行发送操作。

      基于无缓存Channels的发送和接收操作将导致两个goroutine做一次同步操作。因为这个原因,无缓存Channels有时候也被称为同步Channels。当通过一个无缓存Channels发送数据时,接收者收到数据发生在再次唤醒唤醒发送者goroutine之前;在讨论并发编程时,当我们说x事件在y事件之前发生(happens before),我们并不是说x事件在时间上比y时间更早;我们要表达的意思是要保证在此之前的事件都已经完成了。

    ch = make(chan int)    // unbuffered channel
    ch = make(chan int, 0) // unbuffered channel

    串联的Channels

    Channels也可以用于将多个goroutine连接在一起,一个Channel的输出作为下一个Channel的输入。这种串联的Channels就是所谓的管道(pipeline)

    func main() {
        naturals := make(chan int)
        squares := make(chan int)
    
        // Counter
        go func() {
            for x := 0; ; x++ {
                naturals <- x
            }
        }()
    
        // Squarer
        go func() {
            for {
                x := <-naturals
                squares <- x * x
            }
        }()
    
        // Printer (in main goroutine)
        for {
            fmt.Println(<-squares)
        }
    }

      上述是一个永久发送逻辑;但是如果发送者知道,没有更多的值需要发送到channel的话,那么让接收者也能及时知道没有多余的值可接收将是有用的,因为接收者可以停止不必要的接收等待。这可以通过内置的close函数来关闭channel实现:

    当一个channel被关闭后,再向该channel发送数据将导致panic异常。当一个被关闭的channel中已经发送的数据都被成功接收后,后续的接收操作将不再阻塞,它们会立即返回一个零值;但是关闭上面例子中的naturals变量对应的channel并不能终止循环,它依然会收到一个永无休止的零值序列

      有没有办法直接测试一个channel是否被关闭,但是接收操作有一个变体形式:它多接收一个结果,多接收的第二个结果是一个布尔值ok,ture表示成功从channels接收到值,false表示channels已经被关闭并且里面没有值可接收。使用这个特性,我们可以修改squarer函数中的循环代码,当naturals对应的channel被关闭并没有值可接收时跳出循环,并且也关闭squares对应的channel.

    // Squarer
    go func() {
        for {
            x, ok := <-naturals
            if !ok {
                break // channel was closed and drained
            }
            squares <- x * x
        }
        close(squares)
    }()

        Go语言的range循环可直接在channels上面迭代。使用range循环是上面处理模式的简洁语法,它依次从channel接收数据,当channel被关闭并且没有值可接收时跳出循环。

    func main() {
        naturals := make(chan int)
        squares := make(chan int)
    
        // Counter
        go func() {
            for x := 0; x < 100; x++ {
                naturals <- x
            }
            close(naturals)
        }()
    
        // Squarer
        go func() {
            for x := range naturals {
                squares <- x * x
            }
            close(squares)
        }()
    
        // Printer (in main goroutine)
        for x := range squares {
            fmt.Println(x)
        }
    }

    带缓存的Channels

    带缓存的Channel内部持有一个元素队列。队列的最大容量是在调用make函数创建channel时通过第二个参数指定的

    ch = make(chan string, 3)

       向缓存Channel的发送操作就是向内部缓存队列的尾部插入元素,接收操作则是从队列的头部删除元素。如果内部缓存队列是满的,那么发送操作将阻塞直到因另一个goroutine执行接收操作而释放了新的队列空间。相反,如果channel是空的,接收操作将阻塞直到有另一个goroutine执行发送操作而向队列插入元素

    ch <- "A"
    ch <- "B"
    ch <- "C"

     如果有第四个发送操作将发生阻塞;

      Channel和goroutine的调度器机制是紧密相连的,如果没有其他goroutine从channel接收,发送者——或许是整个程序——将会面临永远阻塞的风险。如果你只是需要一个简单的队列,使用slice就可以了。

    http代理服务器(3-4-7层代理)-网络事件库公共组件、内核kernel驱动 摄像头驱动 tcpip网络协议栈、netfilter、bridge 好像看过!!!! 但行好事 莫问前程 --身高体重180的胖子
  • 相关阅读:
    Windows自带Android模拟器启动失败
    Xamarin.Android提示找不到mono.Android.Support.v4
    Xamarin提示Build-tools版本过老
    Xamarin Android布局文件没有智能提示
    Xamarin.iOS模拟器调试找不到资源文件
    彻底卸载 RAD Studio 2009/2010/XE+ 的步骤
    Delphi版本号对照
    RAD Studio 2010 环境设置(转)
    C语言写的俄罗斯方块
    字符编解码的故事–ASCII,ANSI,Unicode,Utf-8区别
  • 原文地址:https://www.cnblogs.com/codestack/p/14968448.html
Copyright © 2011-2022 走看看