2017-05-31 78 views
0

我發現,隨着預期的這段代碼不起作用:條件變量和#pragma包的bug

#pragma pack(push, 1) 

class myclass { 
protected: 
    bool mycrasher[1]; // with mycrasher[4] it works! 
    std::mutex mtx; 
    std::condition_variable cv; 

    void thread_func() { 
     std::this_thread::sleep_for(std::chrono::seconds(1)); 

     std::chrono::milliseconds timeout(1000); 
     std::unique_lock<std::mutex> l(mtx, std::defer_lock); 
     while (true) { 
      auto now = std::chrono::system_clock::now(); 
      l.lock(); 
      while (true) { 
       std::cout << "Waiting..." << std::endl; 
       auto result = cv.wait_until(l, now + timeout); 
       std::cout << "Timed out..." << std::endl; 
       if (result == std::cv_status::timeout) 
        break; 
      } 
      l.unlock(); 
     } 
    } 

public: 
    myclass() { 
     std::lock_guard<std::mutex> l(mtx); 
     std::thread *t = new std::thread(&myclass::thread_func, this); 
     t->detach(); 
    }; 

    void start() { 
     std::cout << "myclass started." << std::endl; 
     std::cout << "sizeof(std::mutex) = " << sizeof(std::mutex) << std::endl; 
     std::cout << "sizeof(std::condition_variable) = " << sizeof(std::condition_variable) << std::endl; 
    } 
}; 
#pragma pack(pop) 

int main() { 
    myclass x; 
    x.start(); 
    std::this_thread::sleep_for(std::chrono::seconds(60)); 
} 

我預想的代碼等待一秒鐘的cv.wait_until呼叫,然後重複,但只是掛在電話上。如果我刪除#pragma指令,問題(直觀地)就會消失,因爲我正在打包互斥和CV。然而,當我運行此代碼,我得到:

myclass started. 
sizeof(std::mutex) = 40 
sizeof(std::condition_variable) = 48 

帶或不帶pragma,如此看來包裝是不是真正的問題。

此外,我發現如果我將mycrasher變量與4字節邊界對齊,問題也會消失。同樣,如果我在std::condition_variable cv聲明後移動變量,則問題消失,但在std::mutex mtxstd::condition_variable cv之間移動時,它仍然存在。

爲什麼當CV未正確對齊時,片段掛在cv.wait_until調用上?預計會有性能降低,但不是一個普通的攤位。

在Debian 8系統上用g ++ 4.9.2和g ++ 6.3轉載。

+1

'#pragma pack'只適用於* your *結構,而不適用於您在結構中使用的其他結構。看起來你不能在奇數地址上有互斥或條件變量。也許你可以告訴我們爲什麼*你想使用'#pragma pack',你試圖用它解決什麼問題,我們可以幫你解決這個問題呢? –

+0

@Someprogrammerdude謝謝,我剛剛發現。我想了解*爲什麼它停止工作。這應該是內存密集型應用程序的一部分,因此打包數據旨在壓縮每個字節。 – xmas79

+0

@ xmas79:這可能會適得其反。擠壓每個字節可以將不相關的數據放在同一個緩存行上。這看起來像是另一種過早優化的情況。 – MSalters

回答

1

不要使用包裝,你不應該使用具有互斥和條件變量的結構。如果你認爲你確實需要這樣做,那麼你的方法就有問題,因爲你通常不應該創建許多這些對象,並且不應該在那裏打包。

舉一個簡單的解決辦法,把你的mycrashermtxcv如下:

#pragma pack(push, 1) 
class myclass 
{ 
protected: 
    std::mutex mtx; 
    std::condition_variable cv; 
    bool mycrasher[1]; 

    void thread_func(); 
public: 
    myclass(); 
    void start(); 
}; 
#pragma pack(pop) 

它與啓用包裝預計最有可能無法正常工作的原因是因爲mtx和奇數地址cv結束和一些使用這些代碼的內部代碼有不同的期望。在某些平臺上(例如ARM)可能會崩潰。

作爲一個方面說明,您的代碼不僅具有對齊問題,而且它還會泄漏myclass構造函數中堆上的線程對象。

+0

謝謝,這是我已經發現的。我無法在互斥體和/或cv文檔中找到明確的期望。那麼,爲什麼我不應該使用具有互斥體和條件變量**的結構來做到這一點? – xmas79

+0

@ xmas79,因爲創建許多需要打包的包裝並不正常。這些不應該創建成千上萬。您需要通過調試器逐步查找代碼,以找出數據因意外對齊而損壞的位置。如果問題仍然存在,您也可以嘗試構建程序而不進行優化,然後內部pthread函數期望數據正確對齊。 – Pavel

+0

所以這不是我的錯,它是一個庫錯誤? – xmas79