![]() |
没有腹肌的开水瓶 · Exception in thread ...· 2 月前 · |
![]() |
千年单身的蚂蚁 · Exception in thread ...· 2 月前 · |
![]() |
温柔的汉堡包 · 影视创作开拓党史教育生动课堂---党建网· 8 月前 · |
![]() |
有情有义的汉堡包 · 何谓 青年!_基层视野·学习心得_共产党员网· 1 年前 · |
![]() |
彷徨的热水瓶 · 欧拉芭蕾猫 - 知乎· 1 年前 · |
![]() |
失恋的柿子 · win11-连接vpn后不能访问外网.梯子没 ...· 1 年前 · |
![]() |
慷慨大方的保温杯 · 奈良美智和康多领衔中国嘉德香港春拍_百科文章 ...· 1 年前 · |
Go 语言支持并发,我们只需要通过 go 关键字来开启 goroutine 即可。
goroutine 是轻量级线程,goroutine 的调度是由 Golang 运行时进行管理的。
goroutine 语法格式:
go 函数名( 参数列表 )
go f(x, y, z)
开启一个新的 goroutine:
f(x, y, z)
Go 允许使用 go 语句开启一个新的运行期线程, 即 goroutine,以一个不同的、新创建的 goroutine 来执行一个函数。 同一个程序中的所有 goroutine 共享同一个地址空间。
package main执行以上代码,你会看到输出的 hello 和 world 是没有固定先后顺序。因为它们是两个 goroutine 在执行:
world hello hello world world hello hello world world hello
通道(channel)是用来传递数据的一个数据结构。
通道可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。操作符
<-
用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。
ch <- v // 把 v 发送到通道 ch v := <-ch // 从 ch 接收数据 // 并把值赋给 v
声明一个通道很简单,我们使用chan关键字即可,通道在使用前必须先创建:
ch := make(chan int)注意 :默认情况下,通道是不带缓冲区的。发送端发送数据,同时必须有接收端相应的接收数据。
以下实例通过两个 goroutine 来计算数字之和,在 goroutine 完成计算后,它会计算两个结果的和:
package main输出结果为:
-5 17 12
通道可以设置缓冲区,通过 make 的第二个参数指定缓冲区大小:
ch := make(chan int, 100)
带缓冲区的通道允许发送端的数据发送和接收端的数据获取处于异步状态,就是说发送端发送的数据可以放在缓冲区里面,可以等待接收端去获取数据,而不是立刻需要接收端去获取数据。
不过由于缓冲区的大小是有限的,所以还是必须有接收端来接收数据的,否则缓冲区一满,数据发送端就无法再发送数据了。
注意 :如果通道不带缓冲,发送方会阻塞直到接收方从通道中接收了值。如果通道带缓冲,发送方则会阻塞直到发送的值被拷贝到缓冲区内;如果缓冲区已满,则意味着需要等待直到某个接收方获取到一个值。接收方在有值可以接收之前会一直阻塞。
package main执行输出结果为:
Go 通过 range 关键字来实现遍历读取到的数据,类似于与数组或切片。格式如下:
v, ok := <-ch
如果通道接收不到数据后 ok 就为 false,这时通道就可以使用 close() 函数来关闭。
package main执行输出结果为:
goroutine 是 golang 中在语言级别实现的轻量级线程,仅仅利用 go 就能立刻起一个新线程。多线程会引入线程之间的同步问题,在 golang 中可以使用 channel 作为同步的工具。
通过 channel 可以实现两个 goroutine 之间的通信。
创建一个 channel, make(chan TYPE {, NUM}) TYPE 指的是 channel 中传输的数据类型,第二个参数是可选的,指的是 channel 的容量大小。
向 channel 传入数据, CHAN <- DATA , CHAN 指的是目的 channel 即收集数据的一方, DATA 则是要传的数据。
从 channel 读取数据, DATA := <-CHAN ,和向 channel 传入数据相反,在数据输送箭头的右侧的是 channel,形象地展现了数据从隧道流出到变量里。
qianyang
qia***ng.wang@gmail.com
我们单独写一个 say2 函数来跑 goroutine,并且 Sleep 时间设置长一点,150 毫秒,看看会发生什么:
package main import ( "fmt" "time" func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s, (i+1)*100) func say2(s string) { for i := 0; i < 5; i++ { time.Sleep(150 * time.Millisecond) fmt.Println(s, (i+1)*150) func main() { go say2("world") say("hello")输出结果:
hello 100 world 150 hello 200 hello 300 world 300 hello 400 world 450 hello 500 [Done] exited with code=0 in 2.066 seconds问题来了,say2 只执行了 3 次,而不是设想的 5 次,为什么呢?
原来,在 goroutine 还没来得及跑完 5 次的时候,主函数已经退出了。
我们要想办法阻止主函数的结束,要等待 goroutine 执行完成之后,再退出主函数:
package main import ( "fmt" "time" func say(s string) { for i := 0; i < 5; i++ { time.Sleep(100 * time.Millisecond) fmt.Println(s, (i+1)*100) func say2(s string, ch chan int) { for i := 0; i < 5; i++ { time.Sleep(150 * time.Millisecond) fmt.Println(s, (i+1)*150) ch <- 0 close(ch) func main() { ch := make(chan int) go say2("world", ch) say("hello") fmt.Println(<-ch)我们引入一个信道,默认的,信道的存消息和取消息都是阻塞的,在 goroutine 中执行完成后给信道一个值 0,则主函数会一直等待信道中的值,一旦信道有值,主函数才会结束。
ghppph
158***02109@163.com
杨毅
yan***tl@163.com
Azz
azz***cret.com
gibson1112
185***72536@163.com
无缓冲是同步的,例如 make(chan int) ,就是一个送信人去你家门口送信,你不在家他不走,你一定要接下信,他才会走,无缓冲保证信能到你手上。
有缓冲是异步的,例如 make(chan int, 1) ,就是一个送信人去你家仍到你家的信箱,转身就走,除非你的信箱满了,他必须等信箱空下来,有缓冲的保证信能进你家的邮箱。
修改一下上面笔记中的程序如下:
package main import ( "fmt" "time" func sum(s []int, c chan int) { sum := 0 for _, v := range s { sum += v fmt.Printf("sum:") fmt.Printf("%#v\n", sum) c <- sum // 把 sum 发送到通道 c fmt.Println("after channel pro") // 通道不带缓冲,表示是同步的,只能向通道 c 发送一个数据,只要这个数据没被接收然后所有的发送就被阻塞 func main() { s := []int{7, 2, 8, -9, 4, 0} c := make(chan int) fmt.Println("go [0,3]") go sum(s[:len(s)/2], c) //a //这里开启一个新的运行期线程,这个是需要时间的,本程序继续往下走 fmt.Println("go [3,6]") go sum(s[len(s)/2:], c) //b fmt.Println("go2 [0,3]") go sum(s[:len(s)/2], c) //c fmt.Println("go2 [3,6]") go sum(s[len(s)/2:], c) //d a b c d和main一起争夺cpu的,他们的执行顺序完全无序,甚至里面不同的语句都相互穿插 但无缓冲的等待是同步的,所以接下来a b c d都会执行,一直执行到c <- sum后,开始同步阻塞 因此after channel pro是打印不出来的, 等要打印after channel pro的时候,main就结束了 fmt.Println("go3 start waiting...") time.Sleep(1000 * time.Millisecond) fmt.Println("go3 waited 1000 ms") //因为a b c d都在管道门口等着,这里度一个,a b c d就继续一个,这个结果的顺序可能是acbd aa := <-c bb := <-c fmt.Println(aa) fmt.Println(bb) x, y := <-c, <-c fmt.Println(x, y, x+y)go [0,3] go [3,6] go2 [0,3] go2 [3,6] sum:sum:sum:17 go3 start waiting... sum:-5 go3 waited 1000 ms -5 -5 -10修改成 make(chan int, 2),同时合并:
fmt.Printf("sum:") fmt.Printf("%#v\n", sum)fmt.Printf("sum:%#v\n", sum)可以看到 after channel pro 没有被阻塞了。
go [0,3] go [3,6] go2 [0,3] go2 [3,6] go3 start waiting... sum:-5 sum:17 after channel pro after channel pro sum:17 sum:-5 go3 waited 1000 ms 17 -5 12
酷神oL
916***937@qq.com
xiaoxunnn
860***203@qq.com
package main import ( "fmt" "time" func fibonacci(n int, c chan int) { x, y := 0, 1 for i := 0; i < n; i++ { c <- x x, y = y, x+y time.Sleep(1000*time.Millisecond) close(c) func main() { c := make(chan int, 10) go fibonacci(cap(c), c) // range 函数遍历每个从通道接收到的数据,因为 c 在发送完 10 个 // 数据之后就关闭了通道,所以这里我们 range 函数在接收到 10 个数据 // 之后就结束了。如果上面的 c 通道不关闭,那么 range 函数就不 // 会结束,从而在接收第 11 个数据的时候就阻塞了。 for i := range c { fmt.Println(i)
wsx
113***0208@qq.com
实例代码:
package main import ( "fmt" "time" func put(c chan int) { for i := 0; i < 10; i++ { c <- i time.Sleep(100 * time.Millisecond) fmt.Println("->放入", i) fmt.Println("=所有的都放进去了!关闭缓冲区,但是里面的数据不会丢失,还能取出。") close(c) func main() { ch := make(chan int, 5) go put(ch) for { time.Sleep(1000 * time.Millisecond) data, ok := <-ch if ok == true { fmt.Println("<-取出", data) } else { break输出结果:
->放入 0 ->放入 1 ->放入 2 ->放入 3 ->放入 4 <-取出 0 ->放入 5 <-取出 1 ->放入 6 <-取出 2 ->放入 7 <-取出 3 ->放入 8 <-取出 4 ->放入 9 =所有的都放进去了!关闭缓冲区,但是里面的数据不会丢失,还能取出。 <-取出 5 <-取出 6 <-取出 7 <-取出 8 <-取出 9放的速度较快,先放满了 4 个,阻塞住;
取的速度较慢,放了4个才开始取,由于缓冲区已经满了,所以取出一个之后,才能再次放入; 放完了之后虽然缓冲区关闭了,但是缓冲区的内容还保留,所以还能继续取出;
![]() |
没有腹肌的开水瓶 · Exception in thread “main“ org.apache.spark.sql.AnalysisException: Cannot write incompatible data to 2 月前 |
![]() |
千年单身的蚂蚁 · Exception in thread “main“ org.apache.spark.sql.AnalysisException: Cannot write incompatible data to 2 月前 |
![]() |
温柔的汉堡包 · 影视创作开拓党史教育生动课堂---党建网 8 月前 |
![]() |
有情有义的汉堡包 · 何谓 青年!_基层视野·学习心得_共产党员网 1 年前 |
![]() |
彷徨的热水瓶 · 欧拉芭蕾猫 - 知乎 1 年前 |