module main
import time
//无返回值的函数
fn do_something() {
println('start do_something...')
time.sleep(2*time.second) //休眠2秒,模拟并发持续的时间
println('end do_something')
}
//有返回值的函数
fn add(x int, y int) int {
println('add并发开始...')
time.sleep(4*time.second) //休眠4秒,模拟并发持续的时间
println('add并发完成')
return x + y
}
fn main() {
//并发调用无返回值的函数
g:=go do_something()
//并发调用带有返回值的函数,完成后返回
g2 := go add(3, 2)
//这段期间主线程可以继续执行别的代码...
g.wait() //阻塞等待线程完成
result := g2.wait() //阻塞等待线程结果返回
println(result)
}
thread线程数组
go表达式实现了并发执行后,然后返回单个结果给主线程。
而thread线程数组实现了并发执行多个线程,然后返回结果数组给主线程,用起来挺简洁明了的。
module main
fn f(x f64) f64 {
y := x * x
return y
}
fn g(shared a []int, i int) {
lock a { //读写锁定,共享变量a
a[i] *= a[i] + 1
}
}
fn main() {
mut r := []thread f64{cap: 10} //轻量级线程数组,每一个线程的返回值类型是f64
for i in 0 .. 10 {
r << go f(f64(i) + 0.5)
}
x := r.wait() //线程数组有一个内置的wait方法,等待线程数组的所有线程全部运行完毕,并返回结果数组
println(x) //[0.25, 2.25, 6.25, 12.25, 20.25, 30.25, 42.25, 56.25, 72.25, 90.25]
shared a := [2 3 5 7 11 13 17]
t := [
go g(shared a, 0)
go g(shared a, 3)
go g(shared a, 6)
go g(shared a, 2)
go g(shared a, 1)
go g(shared a, 5)
go g(shared a, 4)
]
println('threads started')
t.wait()
rlock a {
println(a) //[6, 12, 30, 56, 132, 182, 306]
}
}
错误处理
可以在读写channel中增加or代码块,实现错误处理。
module main
const n = 1000
const c = 100
fn f(ch chan int) {
for _ in 0 .. n {
_ := <-ch
}
ch.close()
}
fn main() {
ch := chan int{cap: c}
go f(ch)
mut j := 0
for {
ch <- j or { //错误处理
break
}
// ch <-j ? //向上抛转错误
j++
}
}
go表达式和go线程数组也支持同样的错误处理方式。
module main
import time
//无返回值的函数
fn do_something() ! { //函数带错误处理
println('start do_something...')
time.sleep(2*time.second) //休眠2秒,模拟并发持续的时间
println('end do_something')
}
//有返回值的函数
fn add(x int, y int) !int { //函数带错误处理
println('add并发开始...')
time.sleep(4*time.second) //休眠4秒,模拟并发持续的时间
println('add并发完成')
return x + y
}
fn main() {
//并发调用无返回值的函数
g :=go do_something()
//并发调用带有返回值的函数,完成后返回
g2 := go add(3, 2)
//这段期间主线程可以继续执行别的代码...
g.wait() or { panic(err) } //支持错误处理的并发
result := g2.wait() or { panic(err) } //支持错误处理的并发
println(result)
}
if条件语句读取chan
fn main() {
mut res := []f64{cap:3}
ch := chan f64{cap: 10}
ch <- 6.75
ch <- -3.25
ch.close()
for _ in 0 .. 3 {
//读取chan成功,则if条件表达式返回true;读取chan失败,chan被关闭,if条件表达式返回false
if x:= <-ch {
res << x
} else {
res << -37.5
}
}
println(res) // 返回[6.75, -3.25, -37.5]
}
fn main() {
ch1 := chan int{}
ch2 := chan f64{}
go do_send(ch1, ch2)
mut a := 0
mut b := 0
for select { // 循环监听channel的写入,写入后执行for代码块,直到所有监听的channel都已关闭
x := <-ch1 {
a += x
}
y := <-ch2 {
a += int(y)
}
} { // for代码块
b++ // 写入3次
println('${b}. event')
}
println(a)
println(b)
}
fn do_send(ch1 chan int, ch2 chan f64) {
ch1 <- 3
ch2 <- 5.0
ch2.close()
ch1 <- 2
ch1.close()
}
主进程阻塞等待
一般来说,主进程执行完毕后,不会等待其他子线程的结果,就直接退出返回,其他子线程也随着终止。
可以在主进程末尾增加阻塞等待子线程的运行结果。
module main
import time
fn main() {
ch := chan int{} //创建同步channel
go fn (c chan int) {
time.sleep(3*time.second)
println('goroutine done')
c.close() //关闭子线程或者写channel
// c <- 333
}(ch)
println('main...')
i := <-ch // 主线程阻塞,等待子线程返回数据或者关闭channel
println('main exit...$i')
}
泛型函数/方法
除了使用标准函数/方法作为go的并发单元,泛型函数/方法也可以。
module main
import time
struct Point {
}
fn (p Point) add[T](c chan int, x T, y T) T {
println('from generic method')
c <- 1
return x + y
}
fn add[T](c chan int, x T, y T) T {
println('from generic function')
c <- 2
return x + y
}
fn main() {
ch := chan int{}
//泛型函数
go add[int](ch, 1, 3)
go add[f64](ch, 1.1, 3.3)
//泛型方法
p := Point{}
go p.add[int](ch, 2, 4)
go p.add[f64](ch, 2.2, 4.4)
i := <-ch
println(i)
time.sleep(1*time.second)
}
module main
import time
struct St {
mut:
x f64
}
fn f(x int, y f64, shared s St,shared a []string, shared m map[string]string) {
time.sleep(50*time.second)
//在这个线程中,如果要对共享变量进行读写,使用lock代码块来锁定,对于读写锁,其他线程只能阻塞等待,不能读写该变量,退出代码块后,自动解锁
lock s,a,m { //可以同时对多个共享变量进行锁定
s.x = x * y
println(s.x)
a[0]='abc'
unsafe {
m['a']='aa'
}
println(a[0])
println(m['a'])
}
return
}
fn main() {
shared s := St{} // struct共享变量
shared a := []string{len:1} // 数组共享变量
shared m := map[string]string // 字典共享变量
unsafe {
m['a']='aa'
}
r := go f(3, 4.0, shared s,shared a, shared m) //把共享变量传递给另一个线程,默认传递引用
r.wait()
//在这个线程中,如果只是要读共享变量,使用rlock代码来锁定,对于只读锁,其他线程可以读该变量,不能修改,退出代码块后,自动解锁
rlock s {
println(s.x)
}
}
函数返回shared类型
struct St {
mut:
x f64
}
fn f() shared St { //函数可以返回shared的变量,用于线程之间的读写锁
shared x := St{ x: 3.25 }
return x
}
fn g(good bool) !shared St { //函数可以返回shared的变量,用于线程之间的读写锁,结合错误处理
if !good {
return error('no shared St created')
}
shared x := St{ x: 12.75 }
return x
}
fn shared_opt_propagate(good bool) !f64 {
shared x := g(good) !
ret := rlock x { x.x }
return ret
}
fn main() {
shared x := f()
val := rlock x { x.x }
println(val)
res := shared_opt_propagate(true) or { 1.25 }
println(res)
}
读写锁表达式
struct St {
mut:
i int
}
fn main() {
shared xx := St{ i: 173 }
shared y := St{ i: -57 }
mut m := 0
m = lock y { y.i } //读写锁表达式
n := rlock xx { xx.i } //读表达式
println(m)
println(n)
}