golang channel到底好在哪?

背景

我们公司总是在推golang,最扯淡的一个同事回答说因为公司都在用所以用,你可以用,但我觉得语言是最没有价值的,最重要的是你要知道他解决了什么事。

并发编程

1并发编程的痛点

问:并发会引发什么?
答:并发会引发线程安全。
问:为什么会引发线程安全?
答:原子性,可见性,有序性。
问:具体说说,原子性,可见性,有序性?
答:原子性是因为线程切换导致,可见性是共享变量,有序性是因为指令重排序。
问:再具体点?
答:cnm。。。

大白话翻译一下:
说白了就是一夫多妻制引发的惨案,一夫是要共享给多个妻的,所以夫有个外号叫共享变量。妻子们就是一个个线程。我住的屋叫内存,妻子们一个个都在cpu屋。

  1. 可见性
    在这里插入图片描述
    我有100万,放在我家的桌子上,每个妻子都能看见,结果每个人同时买了一个50万的包,都觉得我还剩下50万没事,结果给我干破产了!这就是可见性带来的伤害。我意识到了共享变量不应该让太多人知道!

  2. 可见性在这里插入图片描述

    看过宫廷剧的都知道皇上每晚都有选妃的环节,我正常每天晚上都会只在一个人那里,保证原子性。我今晚应该去大老婆那屋,大老婆问老公你还有多少钱,我说100万,这时候突然间二老婆装病非让我过去,来了个线程切换。我又来到了二老婆那里,二老婆也问我有多少钱我说100万,二老婆说我病好了,我就又回大老婆那里了,第二天我发现我透支了100万,原来俩老婆一人昨晚花了100万,这是原子性带来的危害,一晚上只在一个老婆那里就好了。因为一晚上和多个妻子(线程)见面,还是会存在共享变量被多个人知道!

  3. 有序性

2怎么解决痛点

最简单粗暴的方法大家肯定知道!加锁呗!当然还有只读、干掉共享变量两种方式,这里先说简单粗暴的!给你锁住不就完了吗?一个时刻只有一个线程可以操作共享变量。上面一夫多妻制的问题是不是解决了?你没用完你就先锁住呗,比如我就100万,哪个老婆要用哪个老婆就锁住,别人看不见还有多少钱,等你买完东西你把锁释放,别人才能看到还剩下多少。
看上去很美好!但是明眼人都知道一个问题,就100万,你花完我就没钱买东西了,所以几个妻子(线程)就会疯狂的抢锁,谁先拿着谁先花。

抢占锁肯定是有代价的。比如互斥锁,大老婆抢到锁了,二老婆就要放弃cpu产生线程切换。比如自旋锁,大老婆拿到了锁,其他几个老婆就要一直轮询等待锁的释放。

CSP并发模型

Don’t communicate by sharing memory; share memory by communicating. (R. Pike)
“不要以共享内存的方式来通信,相反,要通过通信来共享内存。”

你琢磨一夫多妻这个事啊,无非就是媳妇们都惦记我这点钱,多个人竞争我这点钱。每个媳妇想花钱都要来问我还有多少,那能不能换一个方式,比如我把我的钱放在一个管家那,大媳妇想花钱就找管家那要,管家那有钱就给大媳妇,大媳妇用完了将剩余的钱再给管家,二媳妇花钱就来找管家,这时候钱如果大媳妇还在用,管家就说那你等会,排队,三媳妇这会也来了,也排队,大媳妇钱用完了,还给了管家,管家按照先来后到的顺序依次给二媳妇,三媳妇用。

func main()  {

	guanjia := make(chan int)

	//给管家100w
	go func() {
		guanjia<-100
	}()

	//大老婆花了20万还剩下80万
	go func() {
		for {
			fmt.Println(<-guanjia)
			guanjia<-80
		}
	}()

	//二老婆拿到80万
	go func() {
		for {
			fmt.Println(<-guanjia)
		}
	}()
	
	time.Sleep(2*time.Second)
}

总结

一个是我的几个媳妇不断地问还剩多少钱能不能用,一个媳妇用的时候其他媳妇在哪里轮询,浪费资源,另外一个是找了个管家来调度,他来按照顺序分配该哪个媳妇用了,一个媳妇在用的时候其他媳妇该干嘛干嘛,等着管家叫就好了。

channel的十大用法

https://www.lmlphp.com/user/703/article/item/23153/

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Go 中,可以使用 channel 实现限流,即通过 channel 的容量控制并发执行的协程数。 示例代码如下: ```go package main import ( "fmt" "time" ) func worker(id int, jobs <-chan int, results chan<- int) { for j := range jobs { fmt.Printf("worker %d 执行任务 %d\n", id, j) time.Sleep(time.Second) // 模拟任务执行时间 results <- j * 2 } } func main() { jobs := make(chan int, 100) // 创建任务 channel,缓冲区大小为 100 results := make(chan int, 100) // 创建结果 channel,缓冲区大小为 100 // 创建 3 个 worker 协程,即最多同时执行 3 个任务 for w := 1; w <= 3; w++ { go worker(w, jobs, results) } // 发送 9 个任务到 jobs channel for j := 1; j <= 9; j++ { jobs <- j } close(jobs) // 关闭 jobs channel,表示所有任务已发送完毕 // 收集所有结果 for a := 1; a <= 9; a++ { <-results } } ``` 在上面的示例中,我们创建了一个 `jobs` channel 和一个 `results` channel,用于分别传递任务和结果。我们创建了 3 个 worker 协程,并将 `jobs` 和 `results` channel 分别传递给它们。在主协程中,我们向 `jobs` channel 发送 9 个任务,并关闭 `jobs` channel,表示所有任务已发送完毕。然后我们收集所有结果。 由于 `jobs` channel 的缓冲区大小为 100,即最多可以存储 100 个任务,而 `results` channel 的缓冲区也为 100,即最多可以存储 100 个结果。因此,当 worker 协程数小于等于 3 时,所有任务都可以立即执行;当 worker 协程数大于 3 时,多余的任务会被存储在 `jobs` channel 中,直到有空闲的 worker 协程可以执行它们。这样就实现了限流的功能。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值