2017-08-03 29 views
15

有一個新的實驗功能(可能是C++ 20),它是「同步塊」。該塊提供了一段代碼的全局鎖定。以下是cppreference的示例。C++中新功能「synchronized」塊的優點是什麼?

#include <iostream> 
#include <vector> 
#include <thread> 
int f() 
{ 
    static int i = 0; 
    synchronized { 
     std::cout << i << " -> "; 
     ++i;  
     std::cout << i << '\n'; 
     return i; 
    } 
} 
int main() 
{ 
    std::vector<std::thread> v(10); 
    for(auto& t: v) 
     t = std::thread([]{ for(int n = 0; n < 10; ++n) f(); }); 
    for(auto& t: v) 
     t.join(); 
} 

我覺得這是多餘的。有沒有在synchronized塊之間從上面而這其中任何區別,:

std::mutex m; 
int f() 
{ 
    static int i = 0; 
    std::lock_guard<std::mutex> lg(m); 
    std::cout << i << " -> "; 
    ++i;  
    std::cout << i << '\n'; 
    return i; 
} 

我發現這裏唯一的好處是,我得救了具有全局鎖的麻煩。使用同步塊有更多優點嗎?什麼時候應該首選?

+0

不確定實際情況如何,但cppreference使它聽起來像第一個版本保證在第一個示例中按順序打印,而AFAIK第二個版本沒有。 – NathanOliver

+0

@NathanOliver爲什麼不按順序打印第一個版本?你能解釋一下嗎?據我所知,整套代碼一次只能執行一次,包括打印,這將使一切順利進行。這也是互斥體的情況。 –

+2

「雖然同步塊在全局鎖定下執行,但是實施需要檢查每個塊內的代碼,並針對事務安全的代碼使用樂觀併發(由可用的硬件事務內存備份)事務安全代碼「。 – cpplearner

回答

5

在它的面,所述​​關鍵字是類似std::mutex功能,但是通過引入新的關鍵字和相關聯的語義(例如塊包圍同步區域),它可以更容易,以優化這些區域用於事務記憶。

特別是,std::mutex和朋友原則上或多或少對編譯器不透明,而​​具有明確的語義。編譯器無法確定標準庫std::mutex的功能,並且很難將其轉換爲使用TM。當std::mutex的標準庫實現發生更改時,C++編譯器將會正常工作,因此無法對行爲做出很多假設。

此外,在沒有通過需要​​塊提供一個明確的範圍,這是很難讓編譯器原因有關塊的程度 - 這似乎容易在簡單案件諸如單個作用域lock_guard ,但是有很多複雜的情況,例如,如果鎖定功能在編譯器從未真正知道它可以解鎖的位置處何時轉義出來。

1

鎖通常並不完美。試想一下:

// 
// includes and using, omitted to simplify the example 
// 
void move_money_from(Cash amount, BankAccount &a, BankAccount &b) { 
    // 
    // suppose a mutex m within BankAccount, exposed as public 
    // for the sake of simplicity 
    // 
    lock_guard<mutex> lckA { a.m }; 
    lock_guard<mutex> lckB { b.m }; 
    // oversimplified transaction, obviously 
    if (a.withdraw(amount)) 
     b.deposit(amount); 
} 

int main() { 
    BankAccount acc0{/* ... */}; 
    BankAccount acc1{/* ... */}; 
    thread th0 { [&] { 
     // ... 
     move_money_from(Cash{ 10'000 }, acc0, acc1); 
     // ... 
    } }; 
    thread th1 { [&] { 
     // ... 
     move_money_from(Cash{ 5'000 }, acc1, acc0); 
     // ... 
    } }; 
    // ... 
    th0.join(); 
    th1.join(); 
} 

在這種情況下,事實th0,由acc0移動錢acc1,是 試圖採取acc0.m第一,acc1.m秒,而th1,通過移動錢acc1acc0,試圖首先採取acc1.macc0.m秒可能會使他們陷入僵局。

這個例子過於簡單,並可以通過使用std::lock() 或C++ 17可變參數lock_guard換算後得到解決,但認爲一般情況下 其中一個是使用第三方軟件,不知道在哪裏鎖正在 的採取或釋放。在現實生活中,通過鎖定進行同步很難實現。

事務性內存功能旨在提供比鎖更好的構成 的同步;這是各種優化功能,取決於上下文,但它也是一項安全功能。重寫move_money_from()如下:

void move_money_from(Cash amount, BankAccount &a, BankAccount &b) { 
    synchronized { 
     // oversimplified transaction, obviously 
     if (a.withdraw(amount)) 
     b.deposit(amount); 
    } 
} 

...人們可以從整體上獲得交易的好處,也可以不從 全部獲得,而不會使BankAccount帶有互斥體,並且不會因用戶代碼的衝突請求而導致死鎖。

相關問題