怎么做网站分站/品牌营销策略有哪些方法
主要介绍了C#实现的AES加密解密,结合完整实例形式分析了C#实现的AES算法进行加密与解密的相关技巧,需要的朋友可以参考下。
文件:590m.com/f/25127180-492684636-0c9bf0(访问密码:551685)
以下内容无关:
-------------------------------------------分割线---------------------------------------------
源码分析
Go mutex 源码只有短短的 228 行,但是却包含了很多的状态转变在里面,很不容易看懂,具体可以参见下面的流程图。Mutex 的实现主要借助了 CAS 指令 + 自旋 + 信号量来实现,具体代码我就不再每一行做分析了,有兴趣的可以根据下面流程图配合源码阅读一番。
Lock
Unlock
Unlock
一些例子
-
一个 goroutine 加锁解锁过程
加锁加锁 -
没有加锁,直接解锁问题
没有加锁直接解锁 -
两个 Goroutine,互相加锁解锁
互相加锁解锁 -
三个 Goroutine 等待加锁过程
三个 goroutine 等待加锁
整篇源码其实涉及比较难以理解的就是 Mutex 状态(mutexLocked,mutexWoken,mutexStarving,mutexWaiterShift) 与 Goroutine 之间的状态(starving,awoke)改变, 我们下面将逐一说明。
什么是 Goroutine 排队?
排队
如果 Mutex 已经被一个 Goroutine 获取了锁, 其它等待中的 Goroutine 们只能一直等待。那么等这个锁释放后,等待中的 Goroutine 中哪一个会优先获取 Mutex 呢?
正常情况下, 当一个 Goroutine 获取到锁后, 其他的 Goroutine 开始进入自旋转(为了持有CPU) 或者进入沉睡阻塞状态(等待信号量唤醒). 但是这里存在一个问题, 新请求的 Goroutine 进入自旋时是仍然拥有 CPU 的, 所以比等待信号量唤醒的 Goroutine 更容易获取锁. 用官方话说就是,新请求锁的 Goroutine具有优势,它正在CPU上执行,而且可能有好几个,所以刚刚唤醒的 Goroutine 有很大可能在锁竞争中失败.
于是如果一个 Goroutine 被唤醒过后, 仍然没有拿到锁, 那么该 Goroutine 会放在等待队列的最前面. 并且那些等待超过 1 ms 的 Goroutine 还没有获取到锁,该 Goroutine 就会进入饥饿状态。该 Goroutine 是饥饿状态并且 Mutex 是 Locked 状态时,才有可能给 Mutex 设置成饥饿状态.
获取到锁的 Goroutine Unlock, 将 Mutex 的 Locked 状态解除, 发出来解锁信号, 等待的 Goroutine 开始竞争该信号. 如果发现当前 Mutex 是饥饿状态, 直接将唤醒信号发给第一个等待的 Goroutine
这就是所谓的 Goroutine 排队
排队功能是如何实现的
我们知道在正常状态下,所有等待锁的 Goroutine 按照 FIFO 顺序等待,在 Mutex 饥饿状态下,会直接把释放锁信号发给等待队列中的第一个Goroutine。排队功能主要是通过 runtime_SemacquireMutex, runtime_Semrelease 来实现的.
- runtime_SemacquireMutex – 入队
当 Mutex 被其他 Goroutine 持有时,新来的 Goroutine 将会被 runtime_SemacquireMutex 阻塞。阻塞会分为2种情况:
Goroutine 第一次被阻塞:
当 Goroutine 第一次尝试获取锁时,由于当前锁可能不能被锁定,于是有可能进入下面逻辑
queueLifo := waitStartTime != 0
if waitStartTime == 0 {
waitStartTime = runtime_nanotime()
}
runtime_SemacquireMutex(&m.sema, queueLifo, 1)
由于 waitStartTime 等于 0,runtime_SemacquireMutex 的 queueLifo 等于 false, 于是该 Goroutine 放入到队列的尾部。
Goroutine 被唤醒过,但是没加锁成功,再次被阻塞
由于 Goroutine 被唤醒过,waitStartTime 不等于 0,runtime_SemacquireMutex 的 queueLifo 等于 true, 于是该 Goroutine 放入到队列的头部。