Golang之并发

简介:         Go从语言层面就支持了并行,这让C/C++程序猿们泪流满面一、goroutine         goroutine是Go语言并行设计的核心。goroutine说到底就是线程,但它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些goroutine之间的内存共享。
        Go从语言层面就支持了并行,这让C/C++程序猿们泪流满面
一、goroutine
        goroutine是Go语言并行设计的核心。goroutine说到底就是线程,但它比线程更小,十几个goroutine可能体现在底层就是五六个线程,Go语言内部帮你实现了这些goroutine之间的内存共享。执行goroutine只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩。因此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。
        goroutine是通过Go语言runtime管理的一个线程管理器。goroutine通过go关键字实现,其实就是一个普通的函数,类似于线程函数:
        go hello(a, b, c)
        通过关键字go就启动了一个goroutine,举例说明如下:
        package main
        import (
            "fmt"
            "runtime"
        )
        func say(s string) {
            for i :=0; i                 fmt.Println(s)
            }
        }
        func main() {
            go say("world")    //开一个新的goroutines执行
            say("hello")    //当前goroutines执行
        }
        输出:
        hello
        world
        hello
        world
        hello
        world
        hello
        world
        hello
        上面多个goroutine运行在同一个进程里面,共享内存数据,不过设计上应该遵循:不要通过共享来通信,而要通过通信来共享。
        runtime.Gosched()表示让CPU把时间片让给别人,下次某个时候继续恢复执行该goroutine。
        默认情况下,调度器仅适用单线程,也就是说只实现了并发。想要发挥多核处理器的并行,需要在程序中显示调用runtime.GOMAXPROCS(n)告诉调度器同时使用多个线程。GOMAXPROCS设置了同时运行逻辑代码的系统线程的最大数量,并返回之前的设置。如果n这篇文章。          

二、channels
        goroutine运行在相同的地址空间,因此,访问共享内存必须做好同步。Go语言提供了很好的通信机制channel。channel可以与Unix shell中的双向管道做类比,通过它发送或者接收值。这些值只能是特定的类型:channel类型。定义channel时,也需要定义发送到channel的值的类型。注意:必须使用make创建channel。
        ci := make(chan int)
        cs := make(chan string)
        cf := make(chan interface{})
        channel通过操作符         ch         v :=         我们把这些应用到我们的例子中来:
        package main
        import "fmt"
        func sum(a []int, c chan int) {
            sum := 0
            for _, v:=range a {
                sum += v
            }
            c         }
        func main() {
            a := []int{7, 2, 8, -9, 4, 0}
            c := make(chan int)
            go sum(a[:len(a)/2], c)
            go sum(a[len(a)/2:], c)
            x, y :=             fmt.Println(x, y, x+y)
        }
        默认情况下,channel接收和发送数据的都是阻塞的,除非另一端已经准备好,这样就使得goroutines同步变得更加简单,而不需要显示的lock。所谓阻塞,就是如果读取(value :=
三、Buffered channels
        前面介绍了默认的非缓存类型的channel,不过Go语言也允许指定channel的缓冲大小,很简单,就是channel可以存储多少元素。ch:=make(chan bool, 4),创建了可以存储4个元素的bool型channel。在这个channel中,前4个元素可以无阻塞的写入,当写入第5个元素时,代码将会阻塞,直到其它goroutine从channel中读取一些元素,腾出空间。
        ch := make(chan type, value)
        value == 0 !无缓冲(阻塞)
        value > 0 !缓冲(非阻塞,直到value个元素)
        举例说明如下(修改相应的value值):
        package main
        import "fmt"
        func main() {
            c := make(chan int, 2)        //修改2为1就报错,修改2为3可以正常运行
            c             c             fmt.Println(             fmt.Println(         }

四、Range和Close
        前面例子中,需要读取两次c,不是很方便,也可以通过range,像操作slice或者map一样操作缓存类型的channel。具体请看下例:
        package main
        import (
            "fmt"
        )
        func fibonacci(n int, c chan int) {
            x, y := 1, 1
            for i:+0; i                 c                 x, y = y, x+y
            }
            close(c)
        }
        func main() {
            c := make(chan int, 10)
            go fibonacci(cap(c), c)
            for i := range c {
                fmt.Println(i)
            }
        }
        for i := range c能够不断读取channel里面的数据,直到该channel被显示关闭。从上面代码可以看出,生产者通过关键字close函数显示关闭channel。关闭channel后就无法再发送任何数据了,消费者可以通过语法v, ok :=         需要注意的是:应该在生产者的地方关闭channel,而不是消费者的地方去关闭它,这样容易引起panic。
        另外,channel不像文件之类需要经常去关闭,只有当你确实没有任何数据发送了,或者想显式的结束range循环之类的操作。

五、Select
        前文介绍的都是只有一个channel的情况,如果有多个channel,可以通过关键字select来监听channel上的数据流动。
        select默认是阻塞的,只有当监听的channel中发送或接收可以进行时才会运行,当多个channel都准备好的时候,select是随机选择一个执行的。
        package main
        import "fmt"
        func fibonacci(c, quit chan int) {
            x, y := 1, 1
            for {
                select {
                case c                     x, y = y, x+y
                case                     fmt.Println("quit")
                    return
                }
            }
        }
        func main() {
            c := make(chan int)
            quit := make(chan int)
            go func() {
                for i:=0; i                     fmt.Println(                 }
                quit             } ()
            fibonacci(c, quit)
        }
        在select里面还有default语法,这类似于switch,default就是当监听的channel都没有准备好的时候,默认执行的(select不再阻塞等待channel)。
        select {
        case i :=             //use i
        default:
            //当c阻塞的时候执行这里
        }

六、超时
        有时候会出现goroutine阻塞的情况,可以利用select设置超时来避免整个程序进入阻塞状态,具体通过如下方式实现:
        func main() {
            c := make(chan int)
            c := make(chan bool)
            go func() {
                for {
                    select {
                        case v :=                             println(v)
                        case                         println("timeout")
                        o                         break
                    }
                }
            }()
                    }
        
七、runtime goroutine
        runtime包中有几个处理goroutine的函数。
        (1)Goexit
        退出当前执行的goroutine,但是defer函数还会继续调用。
        (2)Gosched
        让出当前goroutine的执行权限,调度器安排其它等待的任务运行,并在下次某个时候从该位置恢复执行。
        (3)NumCPU
        返回CPU核数量。
        (4)NumGoroutine
        返回正在执行和排队的任务总数。
        (5)GOMAXPROCS
        用来设置可以运行的CPU核数。







相关文章
|
5月前
|
人工智能 安全 算法
Go入门实战:并发模式的使用
本文详细探讨了Go语言的并发模式,包括Goroutine、Channel、Mutex和WaitGroup等核心概念。通过具体代码实例与详细解释,介绍了这些模式的原理及应用。同时分析了未来发展趋势与挑战,如更高效的并发控制、更好的并发安全及性能优化。Go语言凭借其优秀的并发性能,在现代编程中备受青睐。
165 33
|
4月前
|
存储 Go 开发者
Go 语言中如何处理并发错误
在 Go 语言中,并发编程中的错误处理尤为复杂。本文介绍了几种常见的并发错误处理方法,包括 panic 的作用范围、使用 channel 收集错误与结果,以及使用 errgroup 包统一管理错误和取消任务,帮助开发者编写更健壮的并发程序。
91 4
Go 语言中如何处理并发错误
|
2月前
|
数据采集 Go API
Go语言实战案例:多协程并发下载网页内容
本文是《Go语言100个实战案例 · 网络与并发篇》第6篇,讲解如何使用 Goroutine 和 Channel 实现多协程并发抓取网页内容,提升网络请求效率。通过实战掌握高并发编程技巧,构建爬虫、内容聚合器等工具,涵盖 WaitGroup、超时控制、错误处理等核心知识点。
|
2月前
|
数据采集 消息中间件 编解码
Go语言实战案例:使用 Goroutine 并发打印
本文通过简单案例讲解 Go 语言核心并发模型 Goroutine,涵盖协程启动、输出控制、主程序退出机制,并结合 sync.WaitGroup 实现并发任务同步,帮助理解 Go 并发设计思想与实际应用。
|
6月前
|
数据采集 监控 Go
用 Go 实现一个轻量级并发任务调度器(支持限速)
本文介绍了如何用 Go 实现一个轻量级的并发任务调度器,解决日常开发中批量任务处理的需求。调度器支持最大并发数控制、速率限制、失败重试及结果收集等功能。通过示例代码展示了其使用方法,并分析了核心组件设计,包括任务(Task)和调度器(Scheduler)。该工具适用于网络爬虫、批量请求等场景。文章最后总结了 Go 并发模型的优势,并提出了扩展功能的方向,如失败回调、超时控制等,欢迎读者交流改进。
208 25
|
Shell Go API
Go语言grequests库并发请求的实战案例
Go语言grequests库并发请求的实战案例
|
8月前
|
存储 缓存 安全
Go 语言中的 Sync.Map 详解:并发安全的 Map 实现
`sync.Map` 是 Go 语言中用于并发安全操作的 Map 实现,适用于读多写少的场景。它通过两个底层 Map(`read` 和 `dirty`)实现读写分离,提供高效的读性能。主要方法包括 `Store`、`Load`、`Delete` 等。在大量写入时性能可能下降,需谨慎选择使用场景。
|
11月前
|
存储 负载均衡 监控
如何利用Go语言的高效性、并发支持、简洁性和跨平台性等优势,通过合理设计架构、实现负载均衡、构建容错机制、建立监控体系、优化数据存储及实施服务治理等步骤,打造稳定可靠的服务架构。
在数字化时代,构建高可靠性服务架构至关重要。本文探讨了如何利用Go语言的高效性、并发支持、简洁性和跨平台性等优势,通过合理设计架构、实现负载均衡、构建容错机制、建立监控体系、优化数据存储及实施服务治理等步骤,打造稳定可靠的服务架构。
233 1
|
11月前
|
Go 调度 开发者
探索Go语言中的并发模式:goroutine与channel
在本文中,我们将深入探讨Go语言中的核心并发特性——goroutine和channel。不同于传统的并发模型,Go语言的并发机制以其简洁性和高效性著称。本文将通过实际代码示例,展示如何利用goroutine实现轻量级的并发执行,以及如何通过channel安全地在goroutine之间传递数据。摘要部分将概述这些概念,并提示读者本文将提供哪些具体的技术洞见。
|
12月前
|
Java 大数据 Go
Go语言:高效并发的编程新星
【10月更文挑战第21】Go语言:高效并发的编程新星
348 7