2016-08-11 13 views
3

我正在閱讀Apple的documentation。我想我知道什麼時候選擇一個值類型以及何時選擇一個參考類型,但我回到了Swif101。該文件說:Apple對多線程的引用和值類型的描述

  • 值類型:的數據將在多個線程的代碼中使用。
  • 引用類型:您要創建共享的,可變的狀態

是不是引用類型還可以在多個線程共享?這兩條線有什麼不同?

回答

1

正如其他人所指出的,引用類型總是傳遞一個指向該對象的指針,這非常適合您想要「共享的,可變的狀態」(如您所引用的文檔所述)。顯然,如果你在多個線程之間變異/訪問一個引用類型,確保同步你的訪問(通過專用串行隊列,讀寫器模式,鎖等)。

雖然值類型有點複雜。是的,正如其他人指出的那樣,如果你將一個值類型作爲參數傳遞給一個方法,然後在另一個線程上做一些事情,那麼你本質上就是使用該值類型的副本(Josh關於copy-on-儘管如此,儘管如此)。這確保了傳遞給該方法的對象的完整性。沒關係(並且已經被其他答案充分涵蓋)。

但是,當你處理閉包時它變得更加複雜。舉個例子,如下:

struct Person { 
    var firstName: String 
    var lastName: String 
} 

var person = Person(firstName: "Rob", lastName: "Ryan") 

DispatchQueue.global().async { 
    Thread.sleep(forTimeInterval: 1) 
    print("1: \(person)") 
} 

person.firstName = "Rachel" 
Thread.sleep(forTimeInterval: 2) 
person.lastName = "Moore" 
print("2: \(person)") 

很顯然,你一般不會sleep,但我這樣做是爲了說明這一點:即,即使我們所面對的是值類型和多線程,您在封閉中引用的person相同的實例,因爲您在主線程(或任何運行的線程)上處理,而不是它的副本。如果你正在處理一個可變對象,那不是線程安全的。

我已經設計了這個例子來說明這一點,上面的閉包中的print聲明將報告「Rachel Ryan」,有效地顯示處於不一致狀態的Person值類型的狀態。

對於使用值類型關閉,如果你想享受值語義,你必須改變async調用中使用一個單獨的變量:

let separatePerson = person 
queue.async { 
    Thread.sleep(forTimeInterval: 1) 
    print("1: \(separatePerson)") 
} 

或者,更容易,使用「捕獲列表」,這表明什麼值類型變量應當由封閉捕獲:

queue.async { [person] in 
    Thread.sleep(forTimeInterval: 1) 
    print("1: \(person)") 
} 

在上述任一例子,你現在享受值語義,複製對象和print語句將正確地報告「搶瑞恩」甚至壽原來的person對象正在另一個線程上進行變異。因此,如果您正在處理值類型和閉包,則可以跨線程共享值類型,除非您明確使用捕獲列表(或類似的東西)以享受值語義(即,根據需要複製對象)。

+1

當我第一次在這裏讀到答案時,我認爲在任何情況下值類型都會產生多線程問題。但是,如果您捕獲/複製,這只是正確的。如果你正在操作* exact * **相同​​的**值,那麼你**有**問題... – Honey

+0

*值類型有點複雜,但。是的,正如其他人指出的那樣,如果你將一個值類型作爲參數傳遞給一個方法,然後在另一個線程上做一些事情* < - 你不是在寫一些有點混亂的東西嗎?當傳遞給同一個線程的另一個函數時,該結構不能被突變。假設我將一個結構體傳遞給一個func,func中的參數是不可變的... – Honey

+0

是的,如果將值類型傳遞給方法,那麼該方法會接收它的一個副本,所以如果你改變了它,複製和原件是安全無損的。但正如我在下一段中所說的,「單獨的線程獲取值類型的副本」的討論更加複雜,因爲在使用閉包時這種複製行爲不會發生。原來的問題不是關於將值類型作爲參數傳遞,而是關於其他線程是否總是獲取值類型的副本。底線,封閉中使用的值類型是_not_複製的(至少沒有捕獲列表)。 – Rob

3

是的,引用是共享如果多個線程使用它們;這正是問題所在。所有線程都將指向內存中相同的實際數據。然後他們需要同步機制來確保獨立線程的讀寫操作不會發生衝突。這些機制在代碼複雜性和性能方面具有成本。

不共享值類型的實例:每個線程都有自己的副本*這意味着每個線程都可以讀取和寫入其實例,而無需擔心其他線程正在做什麼。


*使用標準寫入時複製例外斯威夫特STDLIB類型:如果數據發生突變時,才執行實際的複製。

2

這是混亂的措辭。

值類型:數據將在跨多個線程的代碼中使用。

通過這個,我相信它們的意思是當你希望有許多線程從你的數據中讀取時它是有用的。這是因爲你知道,只要你給一個新線程一個數據副本,你就不會受到任何其他副本冒其他線程發生意外突變的風險。

在這種上下文中使用值類型,您不需要共享可變狀態,可以避免來自處理引用類型的許多類錯誤(競爭條件,死鎖,活鎖等) 。

引用類型:您要創建共享,

只有引用類型可以在線程之間共享,通過使兩個線程可變狀態各保留自己的參照共享實例。

1

Apple建議在跨多個線程使用數據時使用值類型,以避免使用共享實例而不是提升它。

當您爲線程提供值類型的對象時,每個線程都會獲得自己的副本,這反過來又讓您不用擔心同步。

通常,你應該更喜歡讓你的值類型不可變(儘管Swift提供了一個機制讓你編寫變異函數)。當你這樣做時,即使使用引用類型,也沒有同步問題。

0

值類型:數據將用於跨多個線程的代碼中。

數據不會在一次分配的線程上發生變化。不用擔心其他線程,因爲每個線程都有自己的副本。

引用類型:要創建的共享的,可變的狀態

的數據可以由任何線程改變這會影響其他線程,以及因爲的類型的參考其(即指向。相同的數據在內存中)。