2017-02-09 31 views
1

我聽說使用不可變數據類型可以使併發編程更安全。 (例如,請參閱this question。)我使用C++進行編碼並試圖獲得這些好處。但我正在努力理解這個概念。在線程之間共享數據時,我如何從不變性中受益?

如果我創建像一個不可變的數據類型,以便:

struct Immutable 
{ 
public: 
    const int x; 

    Immutable(const int x) 
    : x(x) 
    {} 
} 

我構建它在一個線程中,我怎麼能使用它在另一個線程上;即我可以這樣做:

std::shared_ptr<Immutable> sharedMemory; 

// Thread 1: 
sharedMemory = std::make_shared<Immutable>(1); 

// Thread 2: 
DoSomething(*sharedMemory); 

但我仍然要使用鎖或某種障礙,使此代碼線程安全的,因爲價值指向的共享內存可能不是當我嘗試在訪問它被完全構造線程2.

如何以一種使併發更安全的方式複製線程之間的不可變數據,因爲不變性應該這樣做?

+0

我認爲這個想法是在開始多線程之前設置共享對象。 – Justin

回答

1
// Thread 1: 
sharedMemory = std::make_shared<Immutable>(1); 

// Thread 2: 
DoSomething(*sharedMemory); 

這不是一個不變性的例子。 sharedMemory的共享狀態是而不是不可變。

不變性是兩個不同的線程都讀sharedMemory構建或者線程存在。

如果他們想對其進行更改,他們返回的變化。

不變性意味着所有共享狀態不能改變。您仍然可以將數據傳遞到線程(通過線程參數),或將數據從線程傳遞出去(通過future

您甚至可以製作獨立的可變共享狀態,就像工作線程使用的任務隊列一樣。這裏的隊列本身是可變的,並且是精心編寫的。該工作線程佔用的任務。

但任務只有一成不變的共享狀態下工作,他們通過future是排隊的任務返回的數據返回給其他線程。


可變性的軟形式是期貨。

std::shared_future<std::shared_ptr<Immutable>> sharedMemory = create_shared_memory_async(); 

std::future<void> r = DoSomethingWithSharedMemoryAsync(sharedMemory); 

// in DoSomethingWithSharedMemory 
auto sharedMemoryV = sharedMemory.get(); // blocks until memory is ready 
DoSomething(*sharedMemory); 

這不是完全不可變的共享狀態。


這裏是另一個不純使用不可改變的共享狀態的:

cow_ptr<Document> ptr = GetCurrentDocument(); 

std::future<error_code> print = print_document_async(ptr); 
std::future<error_code> backup = backup_document_async(ptr); 

ptr.write().name = "new name"; 

一個cow_ptr是寫指針的副本。它允許只讀不可變的訪問。

如果你想改變它,你可以調用.write()方法。如果你是唯一擁有共享資源的人,它只是給你寫權限。否則,它克隆資源並確保它是唯一的,然後爲您提供寫入權限。

兩個不同的線程,printbackup線程可以訪問ptr。他們不能更改任何其他線程可以看到的數據(允許編輯它們,但只會修改其本地數據副本)。

回到主線程中,我們將文檔重命名爲新名稱。打印線程和備份線程都不會看到這一點,因爲它們具有不可變(邏輯)副本。

兩個線程訪問相同的ptr變量是不合法的,但他們可以訪問複製ptr變量

如果文檔本身全部由cow_ptr構建而成,則該文檔的「副本」將僅複製內部cow_ptr;即它會原子增加一些參考計數,而不是整個狀態。

修改深層元素將涉及麪包屑;你會想要一個breadcrumb_ptr,記錄達到給定的cow_ptr所需的路線。然後,.write()將繼續複製所有內容到「文檔」的根目錄,可能會替換每個指針(使用.write()調用)。

在這個系統下,我們有能力在線程之間共享O(1)代價的非常大且複雜的數據結構shapeshot,唯一的同步開銷就是引用計數。

這仍然不是純粹的不變性。但在實踐中,這種不可變性的不純形式帶來了許多好處,並允許您有效且安全地完成極其危險或昂貴的事情。

0

如果您有多個線程,並且其中至少一個線程將寫入該變量,則只需要對變量進行同步。對於不可變的對象,你不能寫入它。這意味着您可以擁有儘可能多的線程,因爲數據永遠不會改變,因此無需任何不良影響。

因此,在這種情況下,你將靜態初始化這是在C++ 11及以上的線程安全的對象,或者您會初始化它的線程開始之前,然後與他們分享。