并发

协程

使用go关键字后面跟着函数调用, 可以将这个函数的执行过程放在另一个协程(新手可以粗暴理解为新的进程)

go func(){
    sleep(3); // 等待3秒
    print("Inside!");
}()
print("Outside")

多协程并发任务

如果你有一系列的任务, 并且任务的参数都放在了一个数组内, 那么数组的asyncMap方法可以帮你并发地执行这些任务

var start = now();
var res = [1, 2, 3, 4].asyncMap(e => {
    sleep(3); // 假如计算e*e是一个需要耗时3秒的行为
    return e * e;
}, 4); // 4个并发
print(fmt.Sprintf("Cost time %v, Result: %v", now().Sub(start), res));

将上例的.asyncMap替换成.map即可测试无并发的情况

并发锁

并发map

可参考map, 如

// 并发安全
m = new(sync.Map)

代码块

通过以下方法可以保证并发场景下全局仅有count个协程执行代码内容, count缺省为1

synchronized(lock, [count]) {
    // 代码内容
}

其中lock参数可以为string类型(推荐)或其它可序列化的对象

通道

声明

可以通过以下语句声明一个通道

c = new('chan') // 声明一个不限定类型的通道
c = new('chan', 0) // 声明一个指定类型的通道, 第二个参数用于获取参数
c = new('chan', 0, 1) // 第三个参数用于指定缓冲区的大小
c = new('chan', 0, 1, 'w') // 第四个参数指定了通道是否只读、只写

读取数据

从通道读取数据:

<- c // 阻塞到通道获取到值
rec = (<- c) // 读取并赋值
rec <- c // 从读取值声明一个当前作用域的变量
chanrecv(c) // 阻塞读取
chanrecv(c, true) // 不阻塞(尝试)读取
chanrecv(c, c1, c2...) // 从多个通道读取最先有值传递的
chanrecv(c, c1, 3...) // 传入int或float时, 表示设置读取超时时间

值得一提的是, 以上所有<-都可以换成->, 然后将两边调换即可

写入数据

将数据写入通道:

c <- "hahaha" // 将一个字符串写入通道
"heiheihei" -> c // 同样的效果
chansend(c, 123) // 使用方法写入, 尤其在chan chan xxx类型时必须使用该方法

示例

c = new('chan')
go func(){
    var s = now()
    print("Start listen from chan")
    rec <- c
	printf('Chan get %v after %v', rec, now().Sub(s))
}()
print("Ready to send")
sleep(3)
"zwr" -> c
print("Send over")

PS: 当使用<-->时, 为了区分是读取还是写入操作, 要求双方不能同时为chan类型. 如果双方都是chan类型时[例如 new('chan', new('chan'))声明的用于传输通道的通道] 需要调用chanrecvchansend方法以显式指定操作