无缓冲通道在发送和接收数据时都会阻塞
好处:goroutine 可以在没有明确的锁或竞态变量的情况下进行同步。
package main
import(
"fmt"
"math/rand"
"time"
)
func getNumber(){
var i int = 1
fmt.Println(i)
i++
}
func call(){
go getNumber()
go getNumber()
}
func main() {
call()
sleep := rand.Int63n(100)
time.Sleep(time.Duration(sleep) * time.Millisecond)
}
输出1 1
package main
import(
"fmt"
"math/rand"
"sync"
"time"
)
const(
numberGoroutines = 4
taskLoad = 10
)
var wg sync.WaitGroup
func init(){
rand.Seed(time.Now().Unix())
}
func main(){
tasks := make(chan string,taskLoad)
wg.Add(numberGoroutines)
for gr := 1; gr <= numberGoroutines; gr++{
go worker(tasks,gr)
}
for post :=1;post <= taskLoad;post++ {
tasks <- fmt.Sprintf("Task : %d",post)
}
close(tasks)
wg.Wait()
}
func worker(tasks chan string, worker int) {
// 通知函数已经返回
defer wg.Done()
for {
// 等待分配工作
//每个 goroutine 都会在此阻塞,等待从通道里接收新的工作。一旦接收到返回,就会检查 ok 标志,看通道是否已经清空而且关闭。
task, ok := <-tasks
if !ok {
// 这意味着通道已经空了,并且已被关闭
fmt.Printf("Worker: %d : Shutting Down\n", worker)
return
}
// 显示我们开始工作了
fmt.Printf("Worker: %d : Started %s\n", worker, task)
// 随机等一段时间来模拟工作
sleep := rand.Int63n(100)
time.Sleep(time.Duration(sleep) * time.Millisecond)
// 显示我们完成了工作
fmt.Printf("Worker: %d : Completed %s\n", worker, task)
}
}
输出1 2
坏处:稍不注意就写个死锁,如下
单线程操作无缓冲通道死锁
package main
import "fmt"
func main() {
ch := make(chan int)
ch <- 1 //造成死锁
out := <-ch
fmt.Println(out)
close(ch)
}
粗略看一下上边代码的流程是创建channel-----》存放数据-----》取出数据----------》打印输出---------》关闭channel,流程貌似没错。造成死锁的原因在于
无缓冲的信道在取消息和存消息的时候都会挂起当前的goroutine,而当前只有1个main的线程(也是一个goroutine),所以在进行存放数据的ch <- 1这一行,就会产生锁,导致后边代码无法执行。而整个程序无其它线程来让这个锁释放,自然就形成了一个死锁。
单通道多线程死锁
package main
import "fmt"
var ch chan int = make(chan int)
func main() {
ch := make(chan int)
ch <- 1
var a int
go func(){
a = <-ch
}()
fmt.Println(a)
}
多通道多线程存取不匹配死锁
package main
import "fmt"
var ch1 chan int = make(chan int)
var ch2 chan int = make(chan int)
func say(ch chan int) {
fmt.Println(ch)
ch1 <- <- ch2 // ch1 等待 ch2流出的数据
}
func main() {
go say(ch2)
<- ch1 // 堵塞主线
}
上边的代码有2个goroutine、2个通道,造成死锁的原因为主线等ch1中的数据流出,ch1等ch2的数据流出,但是ch2等待数据流入,两个goroutine都在等,也就是死锁。
package main
import "fmt"
func main() {
c, quit := make(chan int), make(chan int)
go func() {
c <- 1 // c通道的数据没有被其他goroutine读取走,堵塞当前goroutine
quit <- 0 // quit始终没有办法写入数据
}()
<- quit
}
package main
import "fmt"
func main() {
ch := make(chan int)
go func(){
ch <- 1
}()
fmt.Println(len(ch))
}