西维蜀黍

【Golang】源码 - Atomic 包

Definition

// A Value provides an atomic load and store of a consistently typed value.
// The zero value for a Value returns nil from Load.
// Once Store has been called, a Value must not be copied.
//
// A Value must not be copied after first use.
type Value struct {
	v interface{}
}

// ifaceWords is interface{} internal representation.
type ifaceWords struct {
	typ  unsafe.Pointer
	data unsafe.Pointer
}
  ...


【Golang】使用 - Atomic Counters

  ...


【Lock】活锁(Livelock)

活锁(Livelock)

Analygy

考虑一个场景:进程P1占有A资源并请求占用资源B,进程P2占有B资源并请求占用资源A。

如果是等待式的请求,两者都会陷入无尽的等待中。这是死锁(deadlock)

如果请求不是等待式的,而是一旦发现资源已经被占有了,就释放所有等待占用和已经占用的资源,并重新开始。此时P1放弃占有资源A重新开始,P2放弃占有资源B重新开始。则P1、P2可能会出现重复不断的开始-回滚循环。这种情况我们称之为活锁(livelock)

相比死锁,活锁更难检测,也更浪费资源(重复不断的开始-回滚循环)。

  ...


【Lock】自旋锁(Spinlock)

背景

在介绍自旋锁前,我们需要介绍一些前提知识来帮助大家明白自旋锁的概念。

阻塞或唤醒一个Java线程需要操作系统进行 context swtich(e.g., kernel space <-> user space, interrupt handling due to I/O or waiting for synchrnization operation to complete)来完成,

Refer to https://swsmile.info/post/os-context-swtich/ in details.

这种 context 转换需要耗费处理器时间。如果同步代码块中的内容过于简单,context 转换消耗的时间有可能比用户代码执行的时间还要长。

在许多场景中,同步资源的锁定时间很短,为了这一小段时间去切换线程,线程挂起和恢复现场的花费可能会让系统得不偿失。如果物理机器有多个处理器,能够让两个或以上的线程同时并行执行,我们就可以让后面那个请求锁的线程不放弃CPU的执行时间,看看持有锁的线程是否很快就会释放锁。

而为了让当前线程“稍等一下”,我们需让当前线程进行自旋,如果在自旋完成后前面锁定同步资源的线程已经释放了锁,那么当前线程就可以不必阻塞而是直接获取同步资源,从而避免切换线程的开销。这就是自旋锁。

  ...


【Operating System】进程 - 进程间通讯中的问题

一些概念

竞态条件(Race Conditions)

在一些操作系统中,一起协同工作的进程可能共享一块存储空间,任何一个进程可以读或者写这个存储空间。这个存储空间可以是内存中的一块区域,也可以是文件系统中的一个文件。

举个例子,在多任务打印缓存处理器(print spooler)的工作场景中,当一个进程希望打印一个文件时,它会将这个文件的名称输入到一个特定的缓存处理器目录(spooler directory)中。打印机守护进程(printer daemon)会周期性地检查缓存处理器目录中是否存在任何文件名,当存在时,则打印他们,并且将这个文件名从目录中移除。

  ...