使用两个通道模拟实现操作系统中的PV操作。
由于channel有两种接受信息的方法:非阻塞接收数据 和 阻塞接收数据。当使用阻塞接收数据的时候,如果没有该数据则会阻塞,直到接收到数据。
原理如下:
-
当func even执行时,首先P(even)一下,判断上一个输出的数字是否来自func odd。而当func odd完成一次输出后,也会进行V(even)来提醒轮到func even输出数字了。如果func odd没有完成输出,则func even 就被会阻塞。以此方法实现两个consumer交替输出
-
Initial: 由于第一个数字是0,有func even输出的,因此初始化时应先P(even)一下
-
结束,由于最后一个打印的数字是9,是由func odd输出的,因此,在even结束最后一个输出后(输出8后),应该再P(even)一下,而odd 结束最后一个输出后(输出9后),就不应该再P(odd)了,因为这是全局的最后一个输出。
PV操作和代码的对应关系
<-P_odd // P(odd)
P_odd<-1 // V(odd)
<-P_even // P(even)
P_even<-1 // V(even)
代码:
package main
import (
"fmt"
"sync"
)
var (
wg sync.WaitGroup
P_even = make( chan int )
P_odd = make( chan int )
)
func producer( odd chan<-int, even chan<-int ){
defer wg.Done( )
for i:=0;i<10;i++{
if i%2==0{
even<-i;
} else {
odd<-i;
}
}
close(odd)
close(even)
}
func oddConsumer( odd <-chan int){
defer wg.Done( )
for{
value,ok := <-odd
<-P_odd // P(odd): means that function odd have right to consume when function even finish consuming and make a "V(odd)"
if ! ok{
fmt.Printf("Consumer odd, work done\n" )
close(P_even) // function odd is the last output (9), so close channel even, and never need to remind function even to consume
return
}
fmt.Printf( "Consumer odd, get value %d\n" ,value)
P_even<-1 // V(even): means that function odd have consumed the number, and remind function even to consume
}
}
func evenConsumer( even <-chan int){
defer wg.Done()
for{
value,ok := <-even
<-P_even // P(even): means that function even have right to consume when function odd finish consuming and make a "V(even)"
if ! ok{
fmt.Printf( "Consumer even, work done \n")
P_odd<-1 // after func even finish its last number(8), it needs to remind func odd to consume 9
close(P_odd) // func even finish its last number and close its channel
return
}
fmt.Printf( "Consumer even,get value %d\n" ,value)
P_odd<-1 // V(odd): means that function even have consumed the number, and remind function odd to consume
}
}
func main( ){
// make a channel
oddChannel := make( chan int)
evenChannel := make( chan int )
wg.Add( 3 )
go func(){P_even <-0 }() // P(even),
// cause the first number is 0, printed by func even
go producer(oddChannel ,evenChannel)
go oddConsumer( oddChannel )
go evenConsumer( evenChannel)
wg.Wait( )
}