ARTS 第8周| LeetCode 接雨水問題 | Golang 併發安全 map | 什麼是開發能力?

ARTS

ARTS 是陳浩(網名左耳朵耗子)在極客時間專欄裏發起的一個活動,目的是經過分享的方式來堅持學習。golang

每人每週寫一個 ARTS:Algorithm 是一道算法題,Review 是讀一篇英文文章,Technique/Tips 是分享一個小技術,Share 是分享一個觀點。

本週內容

本週的 ARTS 你將看到:算法

  1. LeetCode 第 42 題「接雨水」Trapping Rain Water
  2. 這是沒有文章推薦的一週。
  3. 關於 Go sync.Map 的一些思考。
  4. 開發者的知識儲備和其開發能力中間還隔着無數項目實踐和經驗。

Algorithm

本週的算法題是 LeetCode 第 42 題「接雨水」—— Trapping Rain Water編程

我知道這道題各類五花八門的解法,可是若是解法自己太有技巧性的話,反而失去了練習算法題的目的。因此只列舉兩種我認爲最「直觀」的解法。數組

// brute force
func trap_bf(height []int) int {
    ans := 0
    for i := 0; i < len(height); i++ {
        lm, rm := 0, 0
        for l := i; l >= 0; l-- {
            lm = max(lm, height[l])
        }
        for r := i; r < len(height); r++ {
            rm = max(rm, height[r])
        }
        ans += min(lm, rm) - height[i]
    }
    return ans
}


// 相似 DP 的思想
func trap_dp(height []int) int {
    n := len(height)
    if n == 0 {
        return 0
    }
    ans := 0
    lm, rm := make([]int, n), make([]int, n)
    lm[0] = height[0]
    for i := 1; i < n; i++ {
        lm[i] = max(lm[i-1], height[i])
    }
    rm[n-1] = height[n-1]
    for i := n-2; i >= 0; i-- {
        rm[i] = max(rm[i+1], height[i])
    }
    for i := 0; i < n; i++ {
        ans += min(rm[i], lm[i]) - height[i]
    }
    return ans
}

Review 文章推薦

本週沒有特別值得推薦的文章。安全

Tip 編程技巧

接下來咱們聊聊關於 Go 語言sync.Map也就是併發安全字典類型的底層實現。併發

sync.Map這個類型在平時開發中用的並非很是多,並且在比較早期的版本中 Go 也並無官方提供一個併發安全的 map. 官方這個作法的緣由從普通 map 在檢測到併發讀寫時的報錯fatal error: concurrent map writes可見一斑。至於爲何不容許直接併發地使用 map,官方 blog 是這樣解釋的:app

After long discussion it was decided that the typical use of maps did not require safe access from multiple goroutines, and in those cases where it did, the map was probably part of some larger data structure or computation that was already synchronized. Therefore requiring that all map operations grab a mutex would slow down most programs and add safety to few. This was not an easy decision, however, since it means uncontrolled map access can crash the program.

後來可能由於呼聲過高,官方仍是提供了一個基於 map 類型的擴展,即sync.Map. 這個擴展的基本思路就是維護兩個 map 類型對象:readdirty,讀去 map 中的 kv 時儘量只從 read 中去讀,向 map 中寫入 kv 的時候會寫到 dirty 中。固然爲了保持 read 和 dirty 的一致會作一些額外的標記工做。好比,當 dirty 中包含太多 read 中沒有的新 kv 的時候就會將 dirty 中的內容直接升級爲 read(即將 dirty 的值直接賦給 read 並置 dirty 爲 nil)。這樣作的最終目的就是分離讀寫,在併發讀取sync.Map中的值的時候無需加鎖,在讀寫併發或者寫寫併發的時候纔會使用鎖,從而下降使用鎖對性能的影響。ide

固然,目前實現方式的代價就是sync.Map適合讀多寫少的狀況場景,若是寫入佔比過大的話就會致使sync.Map性能降低。另外在使用過程當中還要儘可能關注類型安全。post

關於具體的源碼解析能夠看看下面的這兩篇文章,算是比較詳細的中文sync.Map源碼解讀了。性能

  • 由淺入深的聊聊 Golang 的 sync.Map
    介紹 sync.Map 的實現,也對比了 map 類型,不想本身看源碼的話,這篇是很好的替代。由於這篇後半部分都是在羅列源碼,對,強迫你看源碼。。。
  • Go 1.9 sync.Map揭祕
    內容上和上篇文章互補,能夠兩篇結合在一塊兒看。

最後,仍是建議打開這部分的源碼,對照其餘文章來看,效率最高。

Share 靈光一閃

最近在學習一些技術實現細節的時候,在感受到不一樣的技術確實是「相通」的同時,對於一個開發者真正的「開發能力」是什麼這個問題很是迷惑。我想致使這種迷惑的緣由極可能是我所謂的「學習」能夠提高知識量,但不能真正提高我寫代碼的能力。這種「寫代碼的能力」最終仍是要經過不停的項目實踐才能得到的,單純的學習只能得到寫好代碼的前期技能儲備,但實踐纔是沒法逃避的能力提高途徑。

本週閱讀列表

相關文章
相關標籤/搜索