非阻塞式获取
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var c1, c2 chan int
for {
select {
case n := <-c1:
fmt.Println("Received from c1:", n)
case n := <-c2:
fmt.Println("Received from c2:", n)
default:
fmt.Println("Received no value")
}
}
}
使用select+default实现非非阻塞式获取
同时获取两个chan中的消息
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var c1, c2 chan int = generator(), generator()
for {
select {
case n := <-c1:
fmt.Println("Received from c1:", n)
case n := <-c2:
fmt.Println("Received from c2:", n)
}
}
}
func generator() chan int {
out := make(chan int)
go func() {
i := 0
for {
time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond)
out <- i
i++
}
}()
return out
}
c1和c2打印的速度不一样,谁先出数据就会先选择谁,两个同时出就会随机的去选一个
转发
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var c1, c2 chan int = generator(), generator()
w := createWorker(0)
for {
select {
case n := <-c1:
w <- n
case n := <-c2:
w <- n
}
}
}
func generator() chan int {
out := make(chan int)
go func() {
i := 0
for {
time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond)
out <- i
i++
}
}()
return out
}
func createWorker(id int) chan<- int {
c := make(chan int)
go worker(id, c)
return c
}
func worker(id int, c chan int) {
for n := range c {
fmt.Printf("Worker %d received %d\n", id, n)
}
}
这样同样可以做到我们想要的,但是再Select里面收到数据以后,后面所作的事又会阻塞住
再Select里面进行非阻塞的首发
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var c1, c2 chan int = generator(), generator()
var worker = createWorker(0)
n := 0
hasValue := false
for {
var activeWorker chan<- int
if hasValue {
activeWorker = worker
}
select {
case n = <-c1:
hasValue = true
case n = <-c2:
hasValue = true
case activeWorker <- n:
hasValue = false
}
}
}
func generator() chan int {
out := make(chan int)
go func() {
i := 0
for {
time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond)
out <- i
i++
}
}()
return out
}
func createWorker(id int) chan<- int {
c := make(chan int)
go worker(id, c)
return c
}
func worker(id int, c chan int) {
for n := range c {
fmt.Printf("Worker %d received %d\n", id, n)
}
}
现在已经实现了非阻塞式的收发,但是现在还是有问题,如果生成的数据过快,还是会有数据丢失的情况
模拟数据丢失
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var c1, c2 chan int = generator(), generator()
var worker = createWorker(0)
n := 0
hasValue := false
for {
var activeWorker chan<- int
if hasValue {
activeWorker = worker
}
select {
case n = <-c1:
hasValue = true
case n = <-c2:
hasValue = true
case activeWorker <- n:
hasValue = false
}
}
}
func generator() chan int {
out := make(chan int)
go func() {
i := 0
for {
time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond)
out <- i
i++
}
}()
return out
}
func createWorker(id int) chan<- int {
c := make(chan int)
go worker(id, c)
return c
}
func worker(id int, c chan int) {
for n := range c {
time.Sleep(5 * time.Second)
fmt.Printf("Worker %d received %d\n", id, n)
}
}
让activeWorker每五秒收一条数据,消耗速度过慢,中间有些数据是会被跳掉的,会出现数据丢失的情况!
使用slice存放积压的数据
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var c1, c2 chan int = generator(), generator()
var worker = createWorker(0)
var values []int
for {
var activeWorker chan<- int
var activeValue int
if len(values) > 0 {
activeWorker = worker
activeValue = values[0]
}
select {
case n := <-c1:
values = append(values, n)
case n := <-c2:
values = append(values, n)
case activeWorker <- activeValue:
values = values[1:]
}
}
}
func generator() chan int {
out := make(chan int)
go func() {
i := 0
for {
time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond)
out <- i
i++
}
}()
return out
}
func createWorker(id int) chan<- int {
c := make(chan int)
go worker(id, c)
return c
}
func worker(id int, c chan int) {
for n := range c {
time.Sleep(5 * time.Second)
fmt.Printf("Worker %d received %d\n", id, n)
}
}
我们使用slice存放挤压的数据,然后每五秒消费一条数据;解决了数据丢失的问题,但是现在程序并不会自动退出,会一直死循环下去,而且没办法查看现在挤压了多少条数据;也没办法去查看消息是否超时
程序自动退出&输出挤压数据量&超时提醒
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
var c1, c2 chan int = generator(), generator()
var worker = createWorker(0)
var values []int
// 10秒后接收一条数据
after := time.After(10 * time.Second)
// 每秒接到一条数据
tick := time.Tick(time.Second)
for {
var activeWorker chan<- int
var activeValue int
if len(values) > 0 {
activeWorker = worker
activeValue = values[0]
}
select {
case n := <-c1:
values = append(values, n)
case n := <-c2:
values = append(values, n)
case activeWorker <- activeValue:
values = values[1:]
case <-time.After(500 * time.Millisecond):
// 每600毫秒没有拿到数据认为超时
fmt.Println("timeout")
case <-tick:
// 每秒输出当前已经挤压了多少条数据
fmt.Println("queue len = ",len(values))
case <-after:
// 接到数据后结束
fmt.Println("byebye")
return
}
}
}
func generator() chan int {
out := make(chan int)
go func() {
i := 0
for {
time.Sleep(time.Duration(rand.Intn(1500)) * time.Millisecond)
out <- i
i++
}
}()
return out
}
func createWorker(id int) chan<- int {
c := make(chan int)
go worker(id, c)
return c
}
func worker(id int, c chan int) {
for n := range c {
time.Sleep(1 * time.Second)
fmt.Printf("Worker %d received %d\n", id, n)
}
}
总结
- Select的使用
- 定时器的使用
- 再Select使用Nil chan