2016-04-03 85 views
8

共享內存我不知道什麼是最下邊這個名言地解釋:說明:不要通過共享內存進行通信;通過交流

不要通過共享內存通信;通過通信共享內存。 (R.派克)

The Go Memory Model我可以讀取該:

A中的信道上發送的之前發生相應的接收從該信道完成。 (Golang Spec)

還有一個專門的golang article解釋報價。關鍵的貢獻是一個working example也由安德魯G.

那麼。有時候,太多的交談圍繞....我從內存規格報價得出,也看在工作實例如下:

後goroutine1通過通道,那麼所有的修改發送(任何)到goroutine2 (在內存中的任何位置)通過goroutine1完成後,必須通過同一通道接收後才能看到goroutine2。 (Golang引理由我:)

Therfore我得出下到名言地解釋:

要同步兩個夠程之間的內存訪問,你並不需要通過送內存渠道。足夠好的是從渠道接收(甚至沒有)。您將看到goroutine在發送時(發送到頻道)寫入(任何地方)的任何更改。 (當然,假設沒有其它夠程被寫入相同的內存。)更新(2)2017年8月26日

我有實際上是兩個問題:

1)我的結論正確?

2)我的解釋有幫助嗎?

更新(1) 我假定無緩衝通道。讓我們首先限制自己,避免以太多未知數來徹底改變自己。

請,我們還集中在兩個夠程通信的單信道及相關的記憶效應,而不是最佳做法的一個簡單的用例 - 這超出了這個問題的範圍。

爲了更好地理解我的問題的範圍,假設goroutines可以訪問任何類型的內存結構 - 不僅僅是原始內存結構 - 它可以是一個很大的內存結構,它可以是字符串,映射,數組等等。

+0

TODO寫測試 – honzajde

回答

2

從本質上講,是的。在通道發送之前分配給變量的任何值都有資格在通道讀取後被觀察,因爲通道操作強加了排序約束。但重要的是要記住等式的另一部分:如果你想保證這些值被觀察到,你必須確保沒有其他人可以在寫入和讀取之間寫入這些變量。顯然,使用鎖定是可能的,但同時也是毫無意義的,因爲如果您已經將鎖定和跨線程內存修改組合在一起,那麼您從渠道獲得哪些好處?你可以像布爾一樣簡單地傳遞一些東西,作爲允許獨佔訪問全局數據的令牌,並且在內存模型保證方面(只要你的代碼沒有錯誤),它是100%正確的,它可能僅僅是一個糟糕的設計,因爲你會沒有充分的理由將所有事情都隱含起來並採取行動,明確地傳遞數據通常會變得更清晰並且不易出錯。

+0

到目前爲止,這解決了我的問題。謝謝。仍然在等待Rob P.說他的部分:) – honzajde

3

我不這麼認爲。要點不是用鎖或其他併發原語來保護一個固定的存儲器地址,你可以通過設計來設計程序,使得只有一個執行流被允許訪問該存儲器

實現這一點的簡單方法是通過通道共享內存的引用。 一旦你通過頻道發送你的參考忘記它。通過這種方式,只有使用該通道的例程才能訪問它。

+0

是的,其要點是關於完全的東西 - 正如你在第二段中所描述的那樣。但是,如果我錯了,我錯了嗎? – honzajde

10

這個着名的引用可能有點混亂,如果採取太litteralterally。讓我們把它分解到它的多種鹼性成分,並正確地定義他們:

Don't communicate by sharing memory; share memory by communicating 
     ---- 1 ---- ------ 2 ----- ---- 3 ----- ----- 4 ----- 
  1. 這意味着,不同的執行線程將其他線程的狀態的變化通過讀取內存將被某處修改通知其他。 這是一個完美的例子(儘管對於進程而不是線程)是POSIX共享內存API:http://man7.org/linux/man-pages/man7/shm_overview.7.html。 這種技術需要適當的同步,因爲數據競賽可以很容易地發生。
  2. 這意味着確實存在一部分物理或虛擬內存,可以從多個線程中修改,也可以從這些線程中讀取。沒有明確的所有權概念,內存空間同樣可以從所有線程訪問。
  3. 這是完全不同的。在Go中,像上面一樣共享內存是可能的,並且數據競速可以非常容易地發生,所以這實際上意味着修改goroutine中的變量,不管是像int這樣的簡單值,還是像地圖這樣的複雜數據結構,並通過通道機制將值或指針指向不同的goroutine發送給所有者。理想情況下,沒有共享空間,每個goroutine只能看到它擁有的部分內存。
  4. 這裏的通信意味着只有一個隊列的通道允許一個goroutine從中讀取,因此被通知新內存部分的所有權,而另一個通道發送並接受丟失所有權。這是一個簡單的消息傳遞模式。

總之,什麼報價方式可以這樣概括:

不要使用共享內存和複雜,容易出錯的同步原語overengineer線程間通信,而是使用消息在goroutines(綠色線程)之間傳遞,所以變量和數據可以在這些消息之間順序使用。

在這裏使用單詞序列是值得注意的,因爲它描述了靈感來自goroutines和渠道概念的哲學:Communicating Sequential Processes

+0

感謝您的貢獻@SirDarius。到目前爲止,我問的這個問題讓我很開心:)我希望對其他人也是如此。但是,您對我的問題的答案是1)否2)否? – honzajde

+0

我會說,是的,是的,儘管我不確定結論的語句對於Go開發人員來說聽起來是「慣用的」,如果你明白我的意思。缺失的東西基本上是所有權傳遞的概念。 – SirDarius

+0

答案中的這部分內容真的很具誤導性:「......通過通過渠道機制將值或指針發送給不同的goroutine來放棄所有權」 - 沒有所有權的概念,事實上假設因此如果發送goroutine再次修改數據會導致競爭狀態。 – honzajde

1

1)我的結論是否正確?

我這麼認爲,如果這意味着我希望它有。規範中使用「偶然」術語的語言的原因在於,它爲表達想法提供了明確的溝通形式。

您的描述的問題是,您實際上沒有明確定義您的突發事件訂單。我認爲,儘管你暗示着一個命令。如果你的意思是「在goroutine a之前發生的運行a在特定的同步點上運行,在goroutine b也觀察到同樣的同步點之後goroutine b可以看到」 - 即使在這裏,「同步點」定義不明確 - 儘管我希望你能理解它。正如規範所做的那樣,這種觀點可以在偶然事件中進行定義。

2)我的解釋有幫助嗎?

也許,不熟悉這個主題的人或者正在努力理解描述的偶然風格風格的人可能會發現你的描述更易於解釋。然而,在你的描述中有一些限制和潛在的實際問題,如下所示:

  • 你還沒有嚴格定義你所說的「發送」是同步點。如果你的意思是發送一個無緩衝的頻道,那麼是的,這將創建一個共享同步點,通過規範引入嚴格的發生順序。
  • 雖然假設上述內容屬實,但您已經描述了一個同步點,但這隻能解決原始建議的一個方面。最初的建議包括「所有權轉讓」的概念,並且與創建同步點或偶發事件的關係較少,更多涉及長期維護依賴潛在共享內存的代碼。這個概念是,不是保留對兩個地方某段內存的訪問,而是創建單獨的共享同步點(如互斥鎖),可以將對象的唯一引用從一個所有者傳遞給另一個所有者。以這種方式設計軟件可防止同步點之外的意外修改,這在軟件中經常可以觀察到,這些軟件通過精確使用互斥和廣泛使用共享內存。

互斥或「顯式同步點」是完全相同的建議的相反 - 它們的存儲器中的共享片是,用於進行通信的同步點「通過共享存儲器進行通信」,反之,即使它們具有互斥在底層深處,通道是一種將對象的所有權(發送值)從一個goroutine(發送者)傳遞給另一個goroutine(接收者)的抽象機制。重點在於,忽略通道是如何實現的,用戶通過將其從一個所有者(例程a)傳遞給另一個(例程b)來共享內存(值)。如果您使用通道發送無關數據來創建同步點,則實際上將它用作互斥量,這更接近通過共享內存進行通信(專注於通道),而不是通過通信進行共享(專注於價值)。

我希望這會有所幫助。

+0

感謝您指出緩衝的頻道!我已經更新了我的問題,因此很明顯,我首先假定了無緩衝的渠道。 – honzajde

3

這裏有兩句話;爲了更全面的理解,必須首先單獨查看這些信息,然後將它們聚合在一起,所以: Don't communicate by sharing memory;意思是,不同的線程不應該通過遵守嚴格且易於出錯的內存可見性和同步策略(如內存障礙等)來相互通信。它可以完成,但它可能很快會變得複雜並且數據競爭非常麻煩)。因此,避免遵守手動,程序化的可見性構造主要通過像Java這樣的編程語言進行適當的同步。

share memory by communicating.表示如果一個線程對內存區域進行了任何更改(寫入),它應該將相同的內存區域(內存區域)傳遞給對同一內存區域感興趣的線程;注意這已經將內存範圍限制爲只有兩個線程。

現在閱讀上述兩段,結合golang內存模型,它說,A send on a channel happens before the corresponding receive from that channel completes.發生之前的關係證實,通過第一個goroutine的寫入將會看到第二個goroutine接收到另一端的內存引用這個頻道!

3

讓我簡單點,在這裏。

不要通過共享內存進行通信。

這就好比你在使用線程進行通信時,例如你必須使用變量或互斥鎖來鎖定內存,以防止某人在通信完成之前讀取和寫入內存。通過通信

在Go例程值在信道,而不是阻斷存儲器移動

共享存儲器,發送機通知接收機,用於從該信道接收,因此它通過與接收器通信從得到共享存儲器渠道。