想到啥就写点啥

因为一只猫,我做了一个宠物营养计算器

缘起

为什么会有做这个计算器的想法?是因为前段时间,女朋友的猫准备开始吃自制猫饭。听起来挺浪漫的,自己给猫做饭。但做了两天我就发现,做饭本身根本不是问题,真正的问题是:这份食谱,营养到底够不够?

不是”感觉上健康”就行,而是要有明确答案。蛋白到位了吗,钙磷比对不对,牛磺酸缺口有多大,哪些东西吃多了反而有害。这些问题,靠直觉是回答不了的。

关于async和await的探讨

缘起

最近在看《深入解析C#(第4版)》这本书,看到了第五章,这一章节是关于异步。之前对异步这个概念只能算是一知半解,了解了它的概念和用法,但是对它的实际场景和为了解决什么问题而诞生的是不太清楚的。于是乎,就和小伙伴之间有了一场讨论。

Go语言核心36讲-context.Context类型

使用WaitGroup值的时候,我们最好用“先统一Add,再并发Done,最后Wait”的标准模式来构建协作流程。

如果在调用该值的Wait方法的同时,为了增大其计数器的值,而并发地调用该值的Add方法,那么就很可能会引发 panic。

这就带来了一个问题,如果我们不能在一开始就确定执行子任务的 goroutine 的数量,那么使用WaitGroup值来协调它们和分发子任务的 goroutine,就是有一定风险的。一个解决方案是:分批地启用执行子任务的 goroutine。

Go语言核心36讲-临时对象池sync.Pool

sync.Pool类型可以被称为临时对象池,它的值可以被用来存储临时的对象。与 Go 语言的很多同步工具一样,sync.Pool类型也属于结构体类型,它的值在被真正使用之后,就不应该再被复制了。

这里的“临时对象”的意思是:不需要持久使用的某一类值。这类值对于程序来说可有可无,但如果有的话会明显更好。它们的创建和销毁可以在任何时候发生,并且完全不会影响到程序的功能。

同时,它们也应该是无需被区分的,其中的任何一个值都可以代替另一个。如果你的某类值完全满足上述条件,那么你就可以把它们存储到临时对象池中。

Go语言核心36讲-sync.WaitGroup和sync.Once

之前在一些场合下里,我们使用通道的方式看起来都似乎有些蹩脚。

比如:声明一个通道,使它的容量与我们手动启用的 goroutine 的数量相同,之后再利用这个通道,让主 goroutine 等待其他 goroutine 的运行结束。

这一步更具体地说就是:让其他的 goroutine 在运行结束之前,都向这个通道发送一个元素值,并且,让主 goroutine 在最后从这个通道中接收元素值,接收的次数需要与其他的 goroutine 的数量相同。

这就是下面的coordinateWithChan函数展示的多 goroutine 协作流程。

Go语言核心36讲-原子操作(上)

互斥锁是一个很有用的同步工具,它可以保证每一时刻进入临界区的 goroutine 只有一个。读写锁对共享资源的写操作和读操作则区别看待,并消除了读操作之间的互斥。

条件变量主要是用于协调想要访问共享资源的那些线程。当共享资源的状态发生变化时,它可以被用来通知被互斥锁阻塞的线程,它既可以基于互斥锁,也可以基于读写锁。当然了,读写锁也是一种互斥锁,前者是对后者的扩展。

通过对互斥锁的合理使用,我们可以使一个 goroutine 在执行临界区中的代码时,不被其他的 goroutine 打扰。不过,虽然不会被打扰,但是它仍然可能会被中断(interruption)。

Go语言核心36讲-原子操作(下)

今天的问题是:sync/atomic包中提供了几种原子操作?可操作的数据类型又有哪些?

问题解析

第三个衍生问题: 比较并交换操作与交换操作相比有什么不同?优势在哪里?

回答是:比较并交换操作即 CAS 操作,是有条件的交换操作,只有在条件满足的情况下才会进行值的交换。

Go语言核心36讲-条件变量sync.Cond(上)

前导知识:条件变量与互斥锁

我们常常会把条件变量这个同步工具拿来与互斥锁一起讨论。实际上,条件变量是基于互斥锁的,它必须有互斥锁的支撑才能发挥作用。

条件变量并不是被用来保护临界区和共享资源的,它是用于协调想要访问共享资源的那些线程的。当共享资源的状态发生变化时,它可以被用来通知被互斥锁阻塞的线程。

© 2026 我想探索一下世界

Elegant theme by Shiro · Made by Acris with ❤️