golang中锁mutex的实现

golang中的锁是通过CAS原子操作实现的,Mutex结构如下:

type Mutex struct {

state int32

sema  uint32

}

//state表示锁当前状态,每个位都有意义,零值表示未上锁

//sema用做信号量,通过PV操作从等待队列中阻塞/唤醒goroutine,等待锁的goroutine会挂到等待队列中,并且陷入睡眠不被调度,unlock锁时才唤醒。具体在sync/mutex.go Lock函数实现中。

插播一下sema

虽然在Mutex中就是一个整形字段,但是它是很重要的一环,这个字段就是用于信号量管理goroutine的睡眠和唤醒的。

sema具体实现还没详看,这里大概分析下功能,注意不准确!!

首先sema为goroutine的“调度”提供了一种实现,可以让goroutine阻塞和唤醒

信号量申请资源在runtime/sema.go中semacquire1

信号量释放资源在semrelease1中

首先sema中,一个semaRoot结构和一个全局semtable变量,一个semaRoot用于一个信号量的PV操作(猜测与goroutine调度模型MGP有关,一个Processor挂多个goroutine,对于一个processor下的多个goroutine的需要一个信号量来管理,当然需要一个轻量的锁在goroutine的状态转换时加锁,即下面的lock结构,这个锁与Mutex中的锁不相同的,是sema中自己实现的),多个semaRoot的分配和查找就通过全局变量semtable来管理

type semaRoot struct {

lock  mutex

treap *sudog // root of balanced tree of unique waiters.

nwait uint32 // Number of waiters. Read w/o the lock.

}

var semtable [semTabSize]struct {

root semaRoot

pad  [cpu.CacheLinePadSize - unsafe.Sizeof(semaRoot{})]byte

}

1 让当前goroutine睡眠阻塞是通过goparkunlock实现的,在semacquire1中这样调用:

1) root := semroot(addr)

semroot中是通过信号量地址找到semaRoot结构

2) 略过一段..... 直接到使当前goroutine睡眠位置

首先lock(&root.lock)上锁

然后调用root.queue()让当前goroutine进入等待队列(注意一个信号量管理多个goroutine,goroutine睡眠前,本身的详细信息就要保存起来,放到队列中,也就是在挂到了semaRoot结构的treap上,看注释队列是用平衡树实现的?)

3)调用goparkunlock(&root.lock, waitReasonSemacquire, traceEvGoBlockSync, 4)

最后会调用到gopark,gopark会让系统重新执行一次调度,在重新调度之前,会将当前goroutine,即G对象状态置为sleep状态,不再被调度直到被唤醒,然后unlock锁,这个函数给了系统一个机会,将代码执行权限转交给runtime调度器,runtime会去调度别的goroutine。

2 既然阻塞,就需要有唤醒的机制

唤醒机制是通过semtable结构

sema.go并非专门为mutex锁中的设计的,在mutex中使用的话,是在其它goroutine释放Mutex时,调用的semrelease1,从队列中唤醒goroutine执行。详细没看。

不过根据分析,Mutex是互斥锁,Mutex中的信号量应该是二值信号量,只有0和1。在Mutex中调用Lock,假如执行到semacquire1,从中判断信号量如果为0,就让当前goroutine睡眠,

func cansemacquire(addr *uint32) bool {

for {

v := atomic.Load(addr)

if v == 0 {

return false

}

if atomic.Cas(addr, v, v-1) {

return true

}

}

}

如果不断有goroutine尝试获取Mutex锁,都会判断到信号量为0,会不断有goroutine陷入睡眠状态。只有当unlock时,信号量才会+1,当然不能重复执行unlock,所以这个信号量应该只为0和1。

大概分析了下sema,转回到Mutex中来。

上面说了sema字段的作用,state字段在Mutex中是更为核心的字段,标识了当前锁的一个状态。

state     |31|30|....|      2    |     1      |      0     |

|                |           |      第0位表示当前被加锁,0,unlock,   1 locked

|                |        是否有goroutine已被唤醒,0 唤醒, 1 没有

|           这一位表示当前Mutex处于什么模式,两种模式,0 Normal   1 Starving

第三位表示尝试Lock这个锁而等待的goroutine的个数

先解释下Mutex的normal和starving两种模式,代码中关于Mutex的注释如下

两种模式是为了锁的公平性而实现,摘取网上的一段翻译:http://blog.51cto.com/qiangmzsx/2134786

互斥量可分为两种操作模式:正常和饥饿。

在正常模式下,等待的goroutines按照FIFO(先进先出)顺序排队,但是goroutine被唤醒之后并不能立即得到mutex锁,它需要与新到达的goroutine争夺mutex锁。

因为新到达的goroutine已经在CPU上运行了,所以被唤醒的goroutine很大概率是争夺mutex锁是失败的。出现这样的情况时候,被唤醒的goroutine需要排队在队列的前面。

如果被唤醒的goroutine有超过1ms没有获取到mutex锁,那么它就会变为饥饿模式。

在饥饿模式中,mutex锁直接从解锁的goroutine交给队列前面的goroutine。新达到的goroutine也不会去争夺mutex锁(即使没有锁,也不能去自旋),而是到等待队列尾部排队。

在饥饿模式下,有一个goroutine获取到mutex锁了,如果它满足下条件中的任意一个,mutex将会切换回去正常模式:

1. 是等待队列中的最后一个goroutine

2. 它的等待时间不超过1ms。

正常模式有更好的性能,因为goroutine可以连续多次获得mutex锁;

饥饿模式对于预防队列尾部goroutine一致无法获取mutex锁的问题。

具体实现如下:

在Lock函数中

// Fast path: grab unlocked mutex.

// 1  使用原子操作修改锁状态为locked

if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {

if race.Enabled {

race.Acquire(unsafe.Pointer(m))

}

return

}

Mutex多个goroutine在任何时机都会尝试去获取,Mutex的state又实时在变化,各种场景有点多,这里挑典型的来说。

1) 假设当前mutex处于初始状态,即m.state=0,那么当前goroutine会在这里会直接获取到锁,m.state变为locked,

则m.state = 00...001     上锁了,Not Woken, normal状态。

运气好,一来就获取到,就跟上面说的一样,来时就在cpu里,又赶上锁没人占,天生自带光环,呵呵。

Lock结束return

如果这个goroutine不释放锁,那么然后再来一个goroutine就锁不上了,进入第二步

2) 紧接着一个for循环,大概就是尝试获取锁,求而不得,就睡一会吧,等着被叫醒,醒了看看是不是等的时间太长饿了,饿了就进入starving,starving就会被优先调度了,没有那运气,就只能等了。

var waitStartTime int64

starving := false

awoke := false

iter := 0

old := m.state    //刚才已经设置m.state=001,old也为001

for {

// Don‘t spin in starvation mode, ownership is handed off to waiters

// so we won‘t be able to acquire the mutex anyway.

// old=001,锁着呢

// 然后runtime_canSpin看看能不能自旋啊,就是看传进来的iter,每次循环都是自增

// 自旋条件:多核,GOMAXPROCS>1,至少有另外一个运行的P并且本地队列不空。或许是害怕单核自旋,程序都停了。另外最多自旋4次,iter为4时不会再进if

我们这里考虑多核的情况,会进if

// old在每次if中会重新获取,这里自旋的目的就是等待锁释放,当前占用cpu的goroutine就可以占了,go里面总是尽量让在cpu中的goroutine占用锁

if old&(mutexLocked|mutexStarving) == mutexLocked && runtime_canSpin(iter) {

// Active spinning makes sense.

// Try to set mutexWoken flag to inform Unlock

// to not wake other blocked goroutines.

// 当前awoke为false,但是没有goroutine在等待,那么unlock时,没必要唤醒队列goroutine。

if !awoke && old&mutexWoken == 0 && old>>mutexWaiterShift != 0 &&

atomic.CompareAndSwapInt32(&m.state, old, old|mutexWoken) {

awoke = true

}

runtime_doSpin()     //自旋,执行没用的指令30次

iter++

old = m.state           //old重新获取一次state值,如果有其它goroutine释放了,那么下次循环就不进if了

continue                   //自旋完再循环一次

}

//if出来后,会有两种情况

2.1)其它goroutine  unlock了,上面if判断非Locked跳出,此时 m.state=000, old=000, awoke=false, 没有goroutine在等待,这是最简单的情况了

new := old                  //new=000,   old=000,  m.state=000,  awoke=false,这里初始化new,后面要设置锁状态,m.state设置为new

// Don‘t try to acquire starving mutex, new arriving goroutines must queue.

if old&mutexStarving == 0 {      //new=000, 当前锁并不是starving模式,正在运行的goroutine要占用这个锁,如果是starving模式,当前的goroutine要去排队,把锁让给队列中快饿死的兄弟

new |= mutexLocked              //new=001, 要上锁

}

if old&(mutexLocked|mutexStarving) != 0 {       //old=000, 当前正在跑的这个goroutine要占锁,不会进队列, new=001

new += 1 << mutexWaiterShift

}

// The current goroutine switches mutex to starvation mode.

// But if the mutex is currently unlocked, don‘t do the switch.

// Unlock expects that starving mutex has waiters, which will not

// be true in this case.

if starving && old&mutexLocked != 0 {             //starving=false,只有goroutine在unlock唤醒后,发现等待时间过长,starving才设置为true,因为队列中其它的goroutine都等的有点长了,所以在锁可用时,优先给队列中的goroutine。这个逻辑在后面,当前不进这个if,new=001

new |= mutexStarving

}

if awoke {                       //awoke为false,不去唤醒等待队列, new仍为001

// The goroutine has been woken from sleep,

// so we need to reset the flag in either case.

if new&mutexWoken == 0 {

throw("sync: inconsistent mutex state")

}

new &^= mutexWoken

}

至此new初始化完毕,new=001,要去更改Mutex的锁状态,真正独占锁了

//保险起见,以防在new设置过程中,有其它goroutine更改了锁状态,原子性的设置当前锁状态为new=001,这里就是上锁

if atomic.CompareAndSwapInt32(&m.state, old, new) {

if old&(mutexLocked|mutexStarving) == 0 {                           //old=000,直接break,因为上面是将m.state置为上锁,已经成功了,至此后面逻辑不走了

break // locked the mutex with CAS                                    //回头看2.1,我们如果是自旋次数够了跳出呢?如2.2逻辑

}

// If we were already waiting before, queue at the front of the queue.

queueLifo := waitStartTime != 0

if waitStartTime == 0 {

waitStartTime = runtime_nanotime()

}

runtime_SemacquireMutex(&m.sema, queueLifo)

starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs

old = m.state

if old&mutexStarving != 0 {

// If this goroutine was woken and mutex is in starvation mode,

// ownership was handed off to us but mutex is in somewhat

// inconsistent state: mutexLocked is not set and we are still

// accounted as waiter. Fix that.

if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {

throw("sync: inconsistent mutex state")

}

delta := int32(mutexLocked - 1<<mutexWaiterShift)

if !starving || old>>mutexWaiterShift == 1 {

// Exit starvation mode.

// Critical to do it here and consider wait time.

// Starvation mode is so inefficient, that two goroutines

// can go lock-step infinitely once they switch mutex

// to starvation mode.

delta -= mutexStarving

}

atomic.AddInt32(&m.state, delta)

break

}

awoke = true

iter = 0

} else {

old = m.state

}

2.2)new := old,    此时new=001, old=001, m.state=001, awoke=false (awoke在if中设置为true的情况就不讨论了,太多了。。。。)

// Don‘t try to acquire starving mutex, new arriving goroutines must queue.

if old&mutexStarving == 0 {

new |= mutexLocked                    //new=001

}

if old&(mutexLocked|mutexStarving) != 0 {   //old=001, 当前跑的这个goroutine要进队列,new的第3位到第31位表示队列中goroutine数量,这里+1

new += 1 << mutexWaiterShift                 //new=1001

}

// The current goroutine switches mutex to starvation mode.

// But if the mutex is currently unlocked, don‘t do the switch.

// Unlock expects that starving mutex has waiters, which will not

// be true in this case.

if starving && old&mutexLocked != 0 {        //starving=false,并不需要进入starving模式

new |= mutexStarving

}

if awoke {                                                      //awoke=false

// The goroutine has been woken from sleep,

// so we need to reset the flag in either case.

if new&mutexWoken == 0 {

throw("sync: inconsistent mutex state")

}

new &^= mutexWoken

}

new初始化为1001, old=001

if atomic.CompareAndSwapInt32(&m.state, old, new) {

if old&(mutexLocked|mutexStarving) == 0 {                 //old=001,这里不会break,因为当前的goroutine拿不到锁需要阻塞睡眠

break // locked the mutex with CAS

}

// If we were already waiting before, queue at the front of the queue.

queueLifo := waitStartTime != 0                                     //判断当前goroutine是不是for循环第一次走到这里,是的话,waitStartTime=0

if waitStartTime == 0 {                                                    //queueLifo的true还是false决定了goroutine入队列时,是排队还是插到队头

waitStartTime = runtime_nanotime()

}

runtime_SemacquireMutex(&m.sema, queueLifo)          //当前goroutine入等待队列, 跳到 “注脚1”,更多说明。此时goroutine会阻塞在这,锁释放,如果在队头,才会被唤醒。

starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs   //唤醒时判断是否等待时间过长,超过了1ms,就设置starving为true,“注脚2”更多说明

old = m.state

if old&mutexStarving != 0 {

// If this goroutine was woken and mutex is in starvation mode,

// ownership was handed off to us but mutex is in somewhat

// inconsistent state: mutexLocked is not set and we are still

// accounted as waiter. Fix that.

if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {

throw("sync: inconsistent mutex state")

}

delta := int32(mutexLocked - 1<<mutexWaiterShift)

if !starving || old>>mutexWaiterShift == 1 {

// Exit starvation mode.

// Critical to do it here and consider wait time.

// Starvation mode is so inefficient, that two goroutines

// can go lock-step infinitely once they switch mutex

// to starvation mode.

delta -= mutexStarving

}

atomic.AddInt32(&m.state, delta)

break

}

awoke = true

iter = 0

} else {

old = m.state

}

注脚1    这的runtime_SemacquireMutex是对上面说的sema.go中semacquire1的简单封装,里面最后会调用goPark让当前goroutine让出执行权限给runtime,同时设置当前goroutine为睡眠状态,不参与调度(表现在程序上,就是阻在那了)。

注脚2    1) 这也分两种情况,如果没有超1ms,starving=false

old = m.state              //当前肯定是unlock了,当前goroutine才被唤醒了,所以old至少为000,我们假定为000

if old&mutexStarving != 0    //old不是starving模式,不进if

awoke = true    //充置awoke和iter,重新走循环

iter = 0

///////////////////////////

下次循环中,最后会设置new=001,当前goroutine被唤醒,加锁1,不是starving状态。

最后会在下面这break,跳出Lock函数

if atomic.CompareAndSwapInt32(&m.state, old, new) {

if old&(mutexLocked|mutexStarving) == 0 {

break // locked the mutex with CAS

}

2)如果超了1ms,straving = true

old = m.state              //当前肯定是unlock了,当前goroutine才被唤醒了,所以old至少为000,我们假定为000

if old&mutexStarving != 0    //old不是starving模式,不进if

awoke = true    //充置awoke和iter,重新走循环

iter = 0

///////////////////////////

下次循环 new=101, 锁处于starving模式,当前goroutine被唤醒,已加锁

二  如果处于starving会有什么影响?主要提现在Unlock函数中

// Fast path: drop lock bit.

//先清掉lock位,假设最简单的情况,其它位都为0,则m.state=000, new=000

new := atomic.AddInt32(&m.state, -mutexLocked)

if (new+mutexLocked)&mutexLocked == 0 {

throw("sync: unlock of unlocked mutex")

}

//这里就是starving模式的影响,如果处于starving模式,那么直接走else,从队列头部唤醒一个goroutine。

if new&mutexStarving == 0 {

old := new                   //old = 000

for {

// If there are no waiters or a goroutine has already

// been woken or grabbed the lock, no need to wake anyone.

// In starvation mode ownership is directly handed off from unlocking

// goroutine to the next waiter. We are not part of this chain,

// since we did not observe mutexStarving when we unlocked the mutex above.

// So get off the way.

//如果队列中没有等待的goroutine或者有goroutine已经被唤醒并且抢占了锁(这种情况就如lock中,正好处在cpu中的goroutine在自旋,正好在unlock后,马上抢占了锁),那么就不需要wake等待队列了。

if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken|mutexStarving) != 0 {

return

}

//如果队列中有等着的,并且也没有处在cpu中的goroutine去自旋获取锁,那么就抓住机会从等待队列中唤醒一个goroutine。

// Grab the right to wake someone.

new = (old - 1<<mutexWaiterShift) | mutexWoken

if atomic.CompareAndSwapInt32(&m.state, old, new) {

runtime_Semrelease(&m.sema, false)

return

}

old = m.state

}

} else {

// Starving mode: handoff mutex ownership to the next waiter.

// Note: mutexLocked is not set, the waiter will set it after wakeup.

// But mutex is still considered locked if mutexStarving is set,

// so new coming goroutines won‘t acquire it.

//starving模式,直接从队列头取goroutine唤醒。上面lock函数中没有分析runtime_SemacquireMutex(&m.sema, queueLifo)阻塞被唤醒后,如果lock处于是starving模式,会怎么样,这里分析一下,注脚3

runtime_Semrelease(&m.sema, true)

}

注脚3  首先在unlock函数开头即使清了lock位,cpu中的goroutine也不能获取到锁(因为判断m.state的starving位是饥饿模式,只能队列中等待的goroutine取获取锁,所以cpu中的goroutine会进入等待队列),那么在unlock函数中runtime_Semrelease(&m.sema, true)时,会唤醒队列中一个睡眠的goroutine。

回到lock函数中,此时m.state应为100

runtime_SemacquireMutex(&m.sema, queueLifo)   //在这被唤醒

starving = starving || runtime_nanotime()-waitStartTime > starvationThresholdNs

old = m.state            //old = 100

if old&mutexStarving != 0 {         //lock处于starving中

// If this goroutine was woken and mutex is in starvation mode,

// ownership was handed off to us but mutex is in somewhat

// inconsistent state: mutexLocked is not set and we are still

// accounted as waiter. Fix that.

if old&(mutexLocked|mutexWoken) != 0 || old>>mutexWaiterShift == 0 {

throw("sync: inconsistent mutex state")

}

delta := int32(mutexLocked - 1<<mutexWaiterShift)                //先将当前等待队列减一个

if !starving || old>>mutexWaiterShift == 1 {                              //如果当前队列空了,就把starving清0了

// Exit starvation mode.

// Critical to do it here and consider wait time.

// Starvation mode is so inefficient, that two goroutines

// can go lock-step infinitely once they switch mutex

// to starvation mode.

delta -= mutexStarving

}

atomic.AddInt32(&m.state, delta)      //加锁跳出

break

}

总结:这里只简单说了下互斥锁,另外还有读写锁,不做赘述。互斥锁是在原子操作atomic之上实现的,后面会再详细写下原子操作。

这里先说几个有意思的问题,答案不一定正确,希望大佬指正。

1  一个全局int变量,多核中一个goroutine读,一个写,没有更多操作,需不需要做原子操作。

应该是不需要加的,intel P6处理器在硬件层面上是支持32位变量的load和store的原子性的。另外编译器对于变量的读或写也不会编译成多条指令。

2   一个全局int变量i, 对于多核,两个协程都同时执行i++,需要原子操作吗?

需要的,对于i++,是典型的读改写操作,对于这样的操作,需要CAS原子操作保证原子性。

3  对于一个map,写加原子操作,读要不要加

如果只是读或者写,并且值类型是整形的,应该是不需要atomic原子操作的,这里的意思是对于整形,不会出现写一半,或者读一半的情况,但是不可避免的,会出现这种情况,goroutine1对map写入1,goroutine2读到1,在处理的过程中,goroutine1又重新赋值。

原文地址:https://www.cnblogs.com/myJune/p/10068439.html

时间: 2024-10-27 21:52:23

golang中锁mutex的实现的相关文章

golang中的Mutex和RWMutex

????sync中有两种锁 Mutex和RWMutex,Mutex的读和写必须都进行排队,只能一个完成了在进行下一个,RWMutex读可以并行,写只能一个一个进行,当有读时,需要所有的读全部关闭后才能进行写操作,有写 时,需要等写操作完成了才能进行读操作(读并行,写单一).(sync中有一个map对象是线程安全的,默认的map不是线程安全的). Mutex 先看下面的代码: package main import ( "fmt" "sync" "time&

golang 中 sync.Mutex 和 sync.RWMutex

介绍 golang 中的 sync 包实现了两种锁: Mutex:互斥锁 RWMutex:读写锁,RWMutex 基于 Mutex 实现 Mutex(互斥锁) Mutex 为互斥锁,Lock() 加锁,Unlock() 解锁 在一个 goroutine 获得 Mutex 后,其他 goroutine 只能等到这个 goroutine 释放该 Mutex 使用 Lock() 加锁后,不能再继续对其加锁,直到利用 Unlock() 解锁后才能再加锁 在 Lock() 之前使用 Unlock() 会导

不得不知道的golang之sync.Mutex互斥锁源码分析

针对Golang 1.9的sync.Mutex进行分析,与Golang 1.10基本一样除了将panic改为了throw之外其他的都一样.源代码位置:sync\mutex.go.可以看到注释如下: Mutex can be in 2 modes of operations: normal and starvation. In normal mode waiters are queued in FIFO order, but a woken up waiter does not own the m

深入理解Solaris内核中互斥锁(mutex)与条件变量(condvar)之协同工作原理

在Solaris上写内核模块总是会用到互斥锁(mutex)与条件变量(condvar), 光阴荏苒日月如梭弹指一挥间,Solaris的大船说沉就要沉了,此刻心情不是太好(Orz).每次被年轻的有才华的同事们(比如Letty同学)问起mutex和cv怎么协同工作的,我总是不能给出一个非常清晰的解释.直到今天,看了cv_wait()的源代码之后,我终于可以给他们一个清楚明白的回答了. Solaris的源码无法被公开粘贴出来,幸好还有OpenSolaris的继承者illumos. 先贴cv_wait(

【协作式原创】查漏补缺之Golang中的锁

预备知识 CAS机制 1. 是什么 参考附录3 CAS 是项乐观锁技术,当多个线程尝试使用 CAS 同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试. CAS 操作包含三个操作数 -- 内存位置(V).预期原值(A)和新值(B). 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值.否则,处理器不做任何操作.无论哪种情况,它都会在 CAS 指令之前返回该位置的值.(在 CAS 的一些特殊情

Golang中使用log(二):Golang 标准库log的实现

前一篇文章我们看到了Golang标准库中log模块的使用,那么它是如何实现的呢?下面我从log.Logger开始逐步分析其实现. 其源码可以参考官方地址 1.Logger结构 首先来看下类型Logger的定义: type Logger struct { mu sync.Mutex // ensures atomic writes; protects the following fields prefix string // prefix to write at beginning of each

python多线程中锁的概念

python的锁可以独立提取出来 mutex = threading.Lock() #锁的使用 #创建锁 mutex = threading.Lock() #锁定 mutex.acquire([timeout]) #释放 mutex.release() 概念 好几个人问我给资源加锁是怎么回事,其实并不是给资源加锁, 而是用锁去锁定资源,你可以定义多个锁, 像下面的代码, 当你需要独占某一资源时,任何一个锁都可以锁这个资源 就好比你用不同的锁都可以把相同的一个门锁住是一个道理 import thr

大话Linux内核中锁机制之完成量、互斥量

大话Linux内核中锁机制之完成量.互斥量 在上一篇博文中笔者分析了关于信号量.读写信号量的使用及源码实现,接下来本篇博文将讨论有关完成量和互斥量的使用和一些经典问题. 八.完成量 下面讨论完成量的内容,首先需明确完成量表示为一个执行单元需要等待另一个执行单元完成某事后方可执行,它是一种轻量级机制.事实上,它即是为了完成进程间的同步而设计的,故而仅仅提供了代替同步信号量的一种解决方法,初值被初始化为0.它在include\linux\completion.h定义. 如图8.1所示,对于执行单元A

C++11中的mutex, lock,condition variable实现分析

本文分析的是llvm libc++的实现:http://libcxx.llvm.org/ C++11中的各种mutex, lock对象,实际上都是对posix的mutex,condition的封装.不过里面也有很多细节值得学习. std::mutex 先来看下std::mutex: 包增了一个pthread_mutex_t __m_,很简单,每个函数该干嘛就干嘛. class mutex { pthread_mutex_t __m_; public: mutex() _NOEXCEPT {__m