首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

go语言并发编程

一.并发编程

多进程:是操作系统层面进行并发的基本模式,同时也是开销最大的模式,通过IP+端口来唯一标识一个进程。在操作系统层面,进程其实就是一个0-3G内存块。

多线程:多线程在大部分操作系统上都属于系统层面的并发模式,也是现在我们使用比较多一种有效模式。比进程开销小,其实比起协程的话,器开销还是比较大的,并且在一个高并发模式,效率会比较低

基于回调的非阻塞的/异步IO:这种架构的诞生实际上来源于多线程模式危机,在很多的高并发服务器开发实践中,使用这种多线程的模式会很快耗尽服务器内存和CPU资源。而这种模式通过事件驱动的方式使用异步IO,可以使服务持续运转,尽可能少地使用线程,降低了开销。NodeJS就是最好的实战。

协程:本质上是一种用户态的线程,不需要操作系统的抢占式调度,并且在真正的实现寄存器线程中。因此,开销特别小。

二.协程

go语言在语言级别支持轻量级线程,叫goroutine。go语言标准库提供的所有系统调用操作(包含所有同步IO操作),都会让出CPU给其他goroutine,这样的话,让轻量级的线程的切换管理就不会依赖于系统的进程和线程,也不依赖于CPU的核心数量。

三.goroutine

goroutine是go语言中轻量级线程的实现,由go运行时(runtime)管理。当然,协程在go特别简单,不想进程和线程操作那么复杂。

案例:用协程做一个小案例

packagemain

import"fmt"

funcAdd(x, y int) {

z := x + y

fmt.Println("有名函数", z)

}

funcmain() {

fmt.Println("hello world")

goAdd(1,5)

go func(x, y int) { fmt.Println("匿名函数", x + y)}(1,2)

}

go是定义协程关键字,在一个函数的前加关键字,本次调用就会在一个新的goroutine中运行。当被调用的函数返回时,这个goroutine也就自动结束了。需要注意的是,如果这个函数有返回值的话,那么返回值会被丢弃了。

package main

import "fmt"

func Add(x, y int) {

z := x + y

fmt.Println(z)

}

func main() {

fmt.Println("hello world")

for i := 0; i

go Add(2, 6)

}

}

实际上,上面的代码可能不会有任何Add方法求出的结果的输出,原因是main函数退出时,可能还没有执行go协程,当main函数退出的时候,程序退出,并且程序不会等待其他的goroutine的执行(非主goroutine)结束。

问题简单解决代码如下:

package main

import (

"fmt"

"sync"

)

func Add(x, y int) {

z := x + y

fmt.Println(z)

}

func main() {

var wg sync.WaitGroup //声明一个WaitGroup变量

wg.Add(1)

fmt.Println("hello world")

for i := 0; i

go Add(2, 6)

}

wg.Wait();

}

1.并发通信

并发通信就是多个协程同时去操作同一个共享数据,共享数据是指多个并发单元分别保持对同一个数据的引用,实现对该数据的共享。被共享的数据可能有多种形式,比如内存数据,磁盘文件,网络数据等。实际工作中用得做多是内存数据。

下面的案例是通过全局变量来做一个并发通信:

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

go Count(lock)

}

for {

lock.Lock()

c := counter

lock.Unlock()

runtime.Gosched()

if c >= 10 {

break

}

}

}

在上面的例子中,我们用了10个协程共享了counter,每个协程执行完成之后,counter会去自动+1,由于10个协程是并发执行,故而咱们引入了互斥锁。每次咱们队count做自加操作都要锁定和解锁,在main函数用for循环来检查counter的值,同样加锁解锁,这样的代码很繁琐。不符合咱们go语言的优雅编程。

针对上面这种比较繁琐的代码机制,咱们go语言提供了另种一种通信,即以消息机制而非共享内存做为通信。

消息机制认为每个并发单元是自包含,独立的个体,并且都有自己的变量,但是不同并发单元之间这些变量是不共享。每个并发单元的输入和输出只有一种,那就是消息。

go语言的这种消息机制被称为channel。

2.channel

channel是go语言在语言级别提供的goroutine间的通信,我们可以使用channel在两个或者多个协程之间来传递消息。channel是进程内的通信方式,因此通过channel传递对象的过程和调用函数时的参数传递行为比较一致,比如指针等。

channel是类型相关的,也就是说,一个channel只能传递一种类型的值,这个类型需要在声明channel时指定。可以将其认为是一种类型安全的管道。

channel案例:

package main

import "fmt"

func Count(ch chan int) {

fmt.Println("Counting")

}

func main(){

chs := make([]chan int , 10)

for i := 0; i

chs[i] = make(chan int)

go Count( chs[i])

}

for _, ch := range(chs) {

}

}

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180512G1S0AO00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券