2021-10: 恢复作息
March 14, 2021

终于又恢复作息了,可喜可贺!

大概是从二月底的那一周开始,我的作息就开始有点崩了,直到上周,几乎没有一天早起。而昨天(周日)可能是我崩的最厉害的一天,凌晨 4 点才睡下。这么多年来,我已经清楚地意识到了,熬夜毫无作用,只是用低效的迟迟不肯睡觉来弥补白天的怠惰,用自我感动来掩盖滑坡的纪律。所以,早睡早起才是偿还颓唐一天所负之债的最好手段。

今天周一,5 点一到我就翻身起床了——用的还是老法子,闹钟放在门外,为了不吵到室友,必须马上起来关掉。这样算来实际上我的睡眠时间是不够的——昨天 12 点整才睡下来着,不过这也是无奈之举,人有的时候总得逼自己一把。这一把就扭转乾坤的感觉还是很正面的。

早上工作的效率比晚上高得多,这是不要太显然的一件事情。因为早上起来做事情都有确切的 deadline,而晚上熬夜却毫无底线,总是一拖再拖。另一方面,人总是会更珍惜来之不易的东西,晚睡唾手可得,而早起却是需要决断与牺牲的。

所以接着早上的干劲,我们把周报补一下把。


上周工作上主要是在忙着给 TiDB 修复一个端到端测试中的错误,这件事情我会另外写一篇博客细说,所以就先卖个关子。

周六上午去了上海博物馆看展览,下午和学校的同学聚会;周日上午在准备那天下午的一个给 17 级计算机和软件专业的正在做毕设的同学的一个分享,目前录屏已经上传到 B 站了。

学习上,请见下方笔记:

简单来说,这些机制的动机是为了实现多态和保证内存不要泄露。多态体现在可以用父类指针指向子类(引用类似),而虚函数则是通过动态分发让父类指针的方法调用能调用到子类,为此需要定义虚函数,并通过虚表的机制进行调用;子类可以实现也可以不实现父类的虚函数,但必须实现父类的纯虚函数,因此纯虚函数就是一种必须实现的约定,所以带有纯虚函数的类也叫抽象类,不能实例化;析构函数一般定义为虚函数的原因在于,子类通常会有自己独有的资源,需要被它自己的析构函数特殊处理,因此只有实现为虚函数才能让父类指针在 delete 的时候调用得到,避免资源泄漏。而父类本身的虚析构函数,会在子类析构函数结束后由编译器插入的调用自动调用,避免父类的资源泄漏。因此在子类的析构函数中并不需要显式调用父类析构函数——甚至不能调用,因为这会造成 double free 的问题。

子类实现父类虚函数被称为重写,这种实现需要保证函数名、参数和返回值的一致性;重载就是参数、返回值有不同的重名函数——这个机制实际上由 C++ 的 name mangling 保证,即编译出来的函数名实际上和函数名、参数、返回值的类型都有关系,因此可以有重名的函数;而重定义(又被称作隐藏),实际上就是子类实现父类非虚函数,这样子类指针就可以调用到子类的实现,而不是父类——除非你在调用的时候强行把指针类型转换为父类(注意这里行为与虚函数的不同,虚函数会调用子类)。

C++ 因为本身机制的复杂性,实际上很多正交的概念混合在一起,为了实现多态,为了保证资源不泄露,为了实现类型推导,为了实现 generics 等等,因此有很多东西显得尤为复杂。我虽然平时工作暂时用不上 C++,以前也没有写过 C++ 项目,但理解这些机制还挺有意思的(语言律师警告),而且很多东西也都是通用的。未来看到相关的文章还是会留心看一看的。

另外还读了这样一篇文章,The Railsification of SaaS,讲的是 Autocode 这个后端低代码平台的动机,把 SaaS 服务的 API 组件化、规范化,以期让自动化、程序化能更多地赋能那些非专业的用户。这个方向特别契合我的计算机哲学,所以未来我还会继续关注类似的产品。但是低代码的确是很难做的一件事情,无论有多低,一方面很难覆盖需求本身的复杂性,另一方面也总是存在门槛。或许人工智能才是正途。

此外还在看有栈协程和无栈协程相关的内容,收集了一些资料,但是感觉都不太行(可能因为找的都是中文的资料之原因)。在大致看过之后,我的理解如下:

  1. 有栈协程就是类似 goroutine 这种,每个协程都有自己的栈,因此可以在任何地方打断,只需要保存上下文(pragram counter 和 callee saved register)即可;在任何时候都可以切换回来。因此,有栈协程比较灵活,但切换上下文的开销比较大(相比于无栈协程)。
  2. 无栈协程有一个非常好的演示,就是利用 duff's device 实现的协程。这篇文章我目瞪口呆地读完地——本来就知道 duff's device 很骚,没想到还能这么用。

这里有个 quora 的回答,感觉有点东西

a stackless coroutine always returns something to its caller: either the result, or "no result yet, I'm suspended". It is the caller's responsibility to handle the distinction. If the caller itself is a stackless coroutine, it will immediately return to its own caller "no result yet, I am suspended", etc. Another name for these is "resumable functions". a stackful coroutine is a perfectly ordinary function from the caller's point of view, which has the ability to decide, at some programmer-specified point, to suspend itself, with its entire stack: local variables, caller's local variables, etc, just like a thread does, and ask a userland scheduler to resume another suspended stackful coroutine (or perhaps the same one). Another name for this is "cooperative multitasking" A simple way to remember, stackless coroutine passes control up, stackful coroutine passes control down.

似乎 async/await 这一套语义就是为了无栈协程而引入的,但我现在还不是特别确切地知道,所以接下来会继续研究。


这周的周报非常技术向,毕竟自己主业还是软件研发工程师,这方面每周其实有很多东西可以说,但对非技术背景的读者可能就会更枯燥一些。我别无他法,只能说声抱歉~

希望 11 周每天都能早睡早起(这周周报会 report 每天睡觉和起床的时间),另外我博客挂了有一段时间了,希望能抽空修复一下。