go語言在雲計算時代將會如日中天,還抱着.NET不放的人將會被淘汰。學習go語言和.NET徹底不同,它有很是簡單的runtime 和 類庫。最好的辦法就是將整個源代碼讀一遍,這是我見過最簡潔的系統類庫。讀了以後,你會真正體會到C#的面向對象的表達方式是有問題的,繼承並非必要的東西。相同的問題,在go中有更加簡單的表達。程序員
go runtime 沒有提供任何的鎖,只是提供了一個PV操做原語。獨佔鎖,條件鎖 都是基於這個原語實現的。若是你學習了go,那就就知道如何在windows下高效的方式實現條件鎖定(windows沒有自帶的條件鎖)。golang
我想閱讀源代碼,不能僅僅只看到實現了什麼,還要看到做者的設計思路,還有若是你做爲做者,如何實現。這些纔是真正有用的東西,知識永遠學不完,咱們要鍛鍊咱們的思惟。算法
要寫這篇文章的背景就忽略吧,我已經好久沒有寫博客了,主要緣由是我基本上看不到能讓我有所幫助的博客,更多的是我認爲我也寫不出能對別人有所幫助的文章。爲了寫這篇文章,我仍是花了挺多的心思收集歷史資料, 論壇討論,並去golang-nuts 上諮詢了一些問題。但願對你們有所幫助。windows
一. sync.Mutex 是什麼?設計模式
Mutex是一種獨佔鎖,通常操做系統都會提供這種鎖。可是,操做系統的鎖是針對線程的,golang裏面沒有線程的概念,這樣操做系統的鎖就用不上了。因此,你看go語言的runtime,就會發現,實際上這是一個「操做系統」。若是Mutex還不知道的話,我建議看下面的文章,其中第一篇必看。性能優化
百度百科 mutex http://baike.baidu.com/view/1461738.htm?fromId=1889552&redirected=seachword服務器
信號量:http://swtch.com/semaphore.pdf多線程
還能夠讀一下百度百科 pv 操做:http://baike.baidu.com/view/703687.htmapp
二. golang 最新版本的 sync.Mutexide
你能夠大體掃描一下最新版本的實現,若是你第一眼就看的很懂了,每步的操做?爲何這樣操做?有沒有更加合理的操做?那恭喜你,你的水平已經超過google實現 sync.Mutex 的程序員了,甚至是大部分的程序員,由於這個程序歷經幾年的演化,纔到了今天的樣子,你第一眼就能看的如此透徹,那真的是很了不得。下面的章節是爲沒有看懂的人準備的。
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package sync provides basic synchronization primitives such as mutual
// exclusion locks. Other than the Once and WaitGroup types, most are intended
// for use by low-level library routines. Higher-level synchronization is
// better done via channels and communication.
//
// Values containing the types defined in this package should not be copied.
package sync
import (
"sync/atomic"
"unsafe"
)
// A Mutex is a mutual exclusion lock.
// Mutexes can be created as part of other structures;
// the zero value for a Mutex is an unlocked mutex.
type Mutex struct {
state int32
sema uint32
}
// A Locker represents an object that can be locked and unlocked.
type Locker interface {
Lock()
Unlock()
}
const (
mutexLocked = 1 << iota // mutex is locked
mutexWoken
mutexWaiterShift = iota
)
// Lock locks m.
// If the lock is already in use, the calling goroutine
// blocks until the mutex is available.
func (m *Mutex) Lock() {
// Fast path: grab unlocked mutex.
if atomic.CompareAndSwapInt32(&m.state, 0, mutexLocked) {
if raceenabled {
raceAcquire(unsafe.Pointer(m))
}
return
}
awoke := false
for {
old := m.state
new := old | mutexLocked
if old&mutexLocked != 0 {
new = old + 1<<mutexWaiterShift
}
if awoke {
// The goroutine has been woken from sleep,
// so we need to reset the flag in either case.
new &^= mutexWoken
}
if atomic.CompareAndSwapInt32(&m.state, old, new) {
if old&mutexLocked == 0 {
break
}
runtime_Semacquire(&m.sema)
awoke = true
}
}
if raceenabled {
raceAcquire(unsafe.Pointer(m))
}
}
// Unlock unlocks m.
// It is a run-time error if m is not locked on entry to Unlock.
//
// A locked Mutex is not associated with a particular goroutine.
// It is allowed for one goroutine to lock a Mutex and then
// arrange for another goroutine to unlock it.
func (m *Mutex) Unlock() {
if raceenabled {
_ = m.state
raceRelease(unsafe.Pointer(m))
}
// Fast path: drop lock bit.
new := atomic.AddInt32(&m.state, -mutexLocked)
if (new+mutexLocked)&mutexLocked == 0 {
panic("sync: unlock of unlocked mutex")
}
old := new
for {
// If there are no waiters or a goroutine has already
// been woken or grabbed the lock, no need to wake anyone.
if old>>mutexWaiterShift == 0 || old&(mutexLocked|mutexWoken) != 0 {
return
}
// Grab the right to wake someone.
new = (old - 1<<mutexWaiterShift) | mutexWoken
if atomic.CompareAndSwapInt32(&m.state, old, new) {
runtime_Semrelease(&m.sema)
return
}
old = m.state
}
}
三. 有沒有更加簡潔的實現方法?
有點操做系統知識的都知道,獨佔鎖是一種特殊的PV 操做,就 0 – 1 PV操做。那我想,若是不考慮任何性能問題的話,用信號量應該就能夠這樣實現Mutex:
type Mutex struct {
sema uint32
}
func NewMutex() *Mutex {
var mu Mutex
mu.sema = 1
return &mu
}
func (m *Mutex) Lock() {
runtime_Semacquire(&m.sema)
}
func (m *Mutex2) Unlock() {
runtime_Semrelease(&m.sema)
}
固然,這個實現有點不符合要求。若是有個傢伙不那麼靠譜,加鎖了一次,可是解鎖了兩次。第二次解鎖的時候,應該報出一個錯誤,而不是讓錯誤隱藏。因而乎,咱們想到用一個變量表示加鎖的次數。這樣就能夠判斷有沒有屢次解鎖。因而乎,我就想到了下面的解決方案:
type Mutex struct {這個解決方案除了解決了咱們前面說的重複加鎖的問題外,還對咱們初始化工做作了簡化,不須要構造函數了。注意,這也是golang裏面一個常見的設計模式,叫作 零初始化。
key int32
sema uint32
}
func (m *Mutex) Lock() {
if atomic.AddInt32(&m.key, 1) == 1 {
// changed from 0 to 1; we hold lock
return
}
runtime_Semacquire(&m.sema)
}
func (m *Mutex) Unlock() {
switch v := atomic.AddInt32(&m.key, -1); {
case v == 0:
// changed from 1 to 0; no contention
return
case v == -1:
// changed from 0 to -1: wasn't locked
// (or there are 4 billion goroutines waiting)
panic("sync: unlock of unlocked mutex")
}
runtime_Semrelease(&m.sema)
}
理解一個程序如何工做很簡單,可是,做者的設計思路纔是關鍵,咱們能夠不斷的看源代碼,看別人的實現,咱們能從中學到不少知識與技巧,當遇到相同的問題的時候,咱們也能解決相似的問題。
我我的以爲,做爲一個天朝的程序員,不能僅僅是山寨別人的軟件,學習別人的東西。仍是要能進入一個新的領域,一個未知的領域,還能有所創新。
固然,做者的設計思路咱們很可貴知,咱們看到的只是勞動的結果,可是,咱們能夠這樣問本身,若是我是做者,我怎麼思考這個問題,而後解決這個問題。我發現,用這樣的思惟去考慮問題,有時候能給我不少的啓示。
還有五分鐘就12點了,我必須睡覺了,今天也只能先回答半個問題了。至於爲何不是一個問題,而是半個問題,請聽下回分解。