go语言并发编程

协程

协程(Coroutine)本质上是一种用户态线程,不需要操作系统来进行抢占式调度,且在真正的实现中寄存于线程中,系统开销极小。

package main
import "fmt"
func Count(ch chan int,i int) { 
    fmt.Println("Counting",i)
    ch <- 1
}
func main() {
    chs := make([]chan int 10)
    for i := 0; i < 10; i++ {
        chs[i] = make(chan int)
        go Count(chs[i],i)
    }
    for _, ch := range(chs) {
        <-ch
    }
}

channel声明和定义

//声明一个传递类型为int的channel:
var ch chan int
//或者,我们声明一个map,元素是bool型的channel:
var m map[string] chan bool

//定义一个channel也很简单,直接使用内置的函数make()即可:
ch := make(chan int)

//将一个数据写入(发送)至channel的语法很直观,如下:
ch <- value
//从channel中读取数据的语法是
value := <-ch
select

通过调用select()函数来监控一系列的文件句柄,一旦其中一个文件句柄发生了IO动作,该select()调用就会被返回。

select {
    case <-chan1:
    // 如果chan1成功读到数据,则进行该case处理语句
    case chan2 <- 1:
    // 如果成功向chan2写入数据,则进行该case处理语句
    default:
    // 如果上面都没有成功,则进入default处理流程
}

//我们可以实现一个有趣的程序:
ch := make(chan int, 1)
for {
    select {
    case ch <- 0:
    case ch <- 1:
}
i := <-ch
fmt.Println("Value received:", i)
}
//这个程序实现了一个随机向ch中写入一个0或者1的过程。当然,这是个死循环。
缓冲机制

要创建一个带缓冲的channel,其实也非常容易:

c := make(chan int, 1024)

在调用make()时将缓冲区大小作为第二个参数传入即可,比如上面这个例子就创建了一个大小为1024的int类型channel,即使没有读取方,写入方也可以一直往channel里写入,在缓冲区被填完之前都不会阻塞。

我们可以使用range关键来实现更为简便的循环读取:

for i := range c {
    fmt.Println("Received:", i)
}
超时机制

实现超时机制:

// 首先,我们实现并执行一个匿名的超时等待函数
timeout := make(chan bool, 1)
go func() {
    time.Sleep(1e9) // 等待1秒钟
    timeout <- true
}()
// 然后我们把timeout这个channel利用起来
select {
    case <-ch:
    // 从ch中读取到数据
    case <-timeout:
    // 一直没有从ch中读取到数据,但从timeout中读取到了数据
}

超时机制只是使用了一个channel+延时来完成的。这种写法看起来是一个小技巧,但却是在Go语言开发中避免channel通信超时的最有效方法。

单向channel

单向channel变量的声明非常简单,如下:

var ch1 chan int // ch1是一个正常的channel,不是单向的
var ch2 chan<- float64// ch2是单向channel,只用于写float64数据
var ch3 <-chan int // ch3是单向channel,只用于读取int数据

channel是一个原生类型,因此不仅支持被传递,还支持类型转换。
单向channel和双向channel之间进行转换:

ch4 := make(chan int)
ch5 := <-chan int(ch4) // ch5就是一个单向的读取channel
ch6 := chan<- int(ch4) // ch6 是一个单向的写入channel

从设计的角度考虑,所有的代码应该都遵循“最小权限原则”,从而避免没必要地使用泛滥问题.单向channel也是起到这样的一种契约作用。
单向channel的用法:

func Parse(ch <-chan int) {
    for value := range ch {
        fmt.Println("Parsing value", value)
    }
}

除非这个函数的实现者无耻地使用了类型转换,否则这个函数就不会因为各种原因而对ch
进行写,避免在ch中出现非期望的数据,从而很好地实践最小权限原则。

关闭channel

关闭channel非常简单,直接使用Go语言内置的close()函数即可:

close(ch)
//判断一个channel是否已经被关闭:
x, ok := <-ch
同步锁

Go语言包中的sync包提供了两种锁类型:sync.Mutex和sync.RWMutex。Mutex是最简单的一种锁类型,同时也比较暴力,当一个goroutine获得了Mutex后,其他goroutine就只能乖乖等到这个goroutine释放该Mutex。RWMutex相对友好些,是经典的单写多读模型。
对于这两种锁类型,任何一个Lock()或RLock()均需要保证对应有Unlock()或RUnlock()
典型使用模式如下:

var l sync.Mutex
func foo() {
    l.Lock()
    defer l.Unlock()
    //...
}
全局唯一性操作

从全局的角度只需要运行一次的代码,比如全局初始化操作,Go语言提供了一个Once类型来保证全局的唯一性操作

var a string
var once sync.Once
func setup() {
    a = "hello, world"
}
func doprint() {
    once.Do(setup)
    print(a)
}
func twoprint() {
    go doprint()
    go doprint()
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值