2010-10-20 44 views
0

看來這個問題getsaskedfrequently,但我沒有得出任何明確的結論。我需要決定是否我應該有點幫助實現訪問時,鎖定代碼/修改全局變量時,我有(或必須的!):在文件範圍內 從單個工作線程更新全局變量:我需要互斥鎖嗎?

  • 一個「工人」定義

    • 全局變量線程讀取/從主進程線程調用訪問功能並返回這些全局

    寫入全局變量

  • 通話所以現在的問題是,我應該鎖定與互斥訪問我的全局變量?

    更具體地說,我正在編寫一個C++庫,它使用網絡攝像頭跟蹤紙張上的對象 - 計算機視覺是CPU密集型的,因此性能至關重要。我有一個單個工作線程在Open()函數中被分離出來。該線程處理所有的對象跟蹤。當調用一個Close()函數時,它被終止(間接帶有全局標誌)。

    這感覺就像我只是要求內存損壞,但我沒有觀察到死鎖問題,也沒有遇到任何從這些訪問函數返回的錯誤值。經過幾個小時的研究,我得到的一般印象是,「呃,可能吧,無論如何,。」如果我確實應該使用互斥體,爲什麼我還沒有遇到任何問題呢?

    這裏是我目前的計劃過於簡單化:

    // *********** lib.h *********** 
    // Structure definitions 
    struct Pointer 
    { 
        int x, y; 
    }; 
    // more... 
    
    // API functions 
    Pointer GetPointer(); 
    void Start(); 
    void Stop(); 
    // more... 
    

    實現看起來像這樣...

    // *********** lib.cpp *********** 
    // Globals 
    Pointer p1; 
    bool isRunning = false; 
    HANDLE hWorkerThread; 
    // more... 
    
    // API functions 
    Pointer GetPointer() 
    { 
        // NOTE: my current implementation is actually returning a pointer to the 
        // global object in memory, not a copy of it, like below... 
    
        // Return copy of pointer data 
        return p1; 
    } 
    
    // more "getters"... 
    
    void Open() 
    { 
        // Create worker thread -- continues until Close() is called by API user 
        hWorkerThread = CreateThread(NULL, 0, DoWork, NULL, 0, NULL); 
    } 
    
    void Close() 
    { 
        isRunning = false; 
    
        // Wait for the thread to close nicely or else you WILL get nasty 
        // deadlock issues on close 
        WaitForSingleObject(hWorkerThread, INFINITE); 
    } 
    
    DWORD WINAPI DoWork(LPVOID lpParam) 
    { 
        while (isRunning) 
        { 
        // do work, including updating 'p1' about 10 times per sec 
        } 
    
        return 0; 
    } 
    

    最後,該代碼被從外部可執行文件調用。像這樣的東西(僞代碼):

    // *********** main.cpp *********** 
    int main() 
    { 
        Open(); 
    
        while (<esc not pressed>) 
        { 
        Pointer p = GetPointer(); 
        <wait 50ms or so> 
        } 
        Close(); 
    } 
    

    是否有我應該採取的另一種方法?這個非問題問題讓我今天非常緊張: - /我需要確保這個庫是穩定的並且返回準確的值。任何有識之士將不勝感激。

    感謝

  • +0

    也許有些基礎會有所幫助:http://en.wikipedia.org/wiki/Readers-writer_lock – JoshD 2010-10-21 00:07:21

    +0

    感謝大家的出色答案,尤其是暗示我對讀者 - 作家互斥的想法(似乎這些概念沒有提供)在大學裏得到很多報道......)。我最終使用boost :: shared_mutex來鎖定對主程序循環中修改的每個全局變量的訪問。 (請參閱http://stackoverflow.com/questions/989795/example-for-boost-shared-mutex-multiple-reads-one-write)。如果有人感興趣,我可以提供我最終解決方案的一個簡單例子。 – 2010-10-22 22:36:51

    回答

    1

    我想這取決於你在DoWork()函數中做什麼。我們假設它將點值寫入p1。至少,你有一個競爭條件,將返回無效結果主線程以下可能性:

    假設工作線程要更新P1的值。例如,讓我們將p1的值從(A,B)更改爲(C,D)。這將涉及至少兩個操作,將C存儲在x中,將D存儲在y中。如果主線程決定在GetPointer()函數中讀取p1的值,則它還必須執行至少兩個操作,即爲x加載值併爲y加載值。如果操作的序列是:

    1. 更新線程:器C
    2. 主線程:載荷X(主線程接收C)
    3. 主線程:負載Y(主線程接收B)
    4. 更新線程:存儲D

    主線程將得到點(C,B),這是不正確的。

    這個特殊的問題是沒有很好的利用線程,因爲主線程沒有做任何實際工作。我會使用一個線程和一個像WaitForMultipleObjectsEx這樣的API,它允許您同時等待來自鍵盤stdin句柄的輸入,來自攝像機的I/O事件和超時值。

    +0

    感謝您解釋這個概念。雖然很難驗證這是否發生在我的程序中,但我可以在測試程序中創建這個無效條件(使用線程休眠命令來指示長操作)。雖然對於最終用戶來說,如果我的'x'和'y'值在一兩次迭代時就像這樣碎片化了,我可能無所謂了! :-)最終,我使用Boost庫的'shared_mutex'和編寫器優先鎖......我所要做的就是重構整個程序,呃! – 2010-10-22 22:43:21

    5

    如果只有一個線程訪問(讀取和寫入),那麼就需要一個對象沒有鎖。

    如果一個對象是隻讀的,那麼不需要鎖。 (假設您可以保證在構建過程中只有一個線程訪問該對象)。

    如果有任何線程寫入(更改狀態)的對象。如果有其他線程訪問該對象,則所有訪問(包括讀取和寫入)都必須鎖定。儘管您可能會使用允許多個閱讀器的讀鎖。但寫入操作必須是獨佔的,在狀態改變時,讀者不能訪問該對象。

    +0

    +1 ...除非,當然,你可以自動更新狀態沒有鎖;-) – 2010-10-21 02:32:12

    1

    你不會遇到死鎖,但是你可能會看到一些偶然的錯誤值,並且發生概率極低:因爲讀寫的時間只有納秒的幾分之一,而你每秒只讀取變量50次,碰撞約爲5000萬。

    如果在Intel 64上發生這種情況,「指針」與8字節的邊界對齊,並且在一次操作中讀取和寫入(所有8個字節有一個彙編指令),然後訪問是原子的,不需要互斥體。

    如果任這些條件不能滿足,還有一種可能性,即讀者會得到錯誤的數據。

    我把一個互斥體只是爲了安全起見,因爲它只會使用50次,第二和它不會成爲一個性能問題。

    +2

    你的一些假設是可疑的。他的代碼似乎不大可能以原子方式更新x和y成員,但我想如果CPU支持64位原子存儲操作,編譯器可以這樣優化它。另一個問題是沒有內存屏障,不能保證主線程必須讀取主線程中的內存,並且不能保證更新線程實際上必須寫入內存。如果他可以利用他的CPU/OS提供的原子操作,那麼他不需要互斥鎖。 – karunski 2010-10-21 00:36:36

    0

    由於指針中信息的性質,您可能不會看到問題。如果它正在跟蹤一些運動速度不是很快的對象的座標,並且在讀取過程中位置被更新,那麼座標可能是「小關」,但不足以注意到。

    例如,假設一個更新後,PX爲100,PY是100,您將可以追蹤被攝體移動一點,這樣在下次更新後,PX是102和PY是102.如果你碰巧讀在本次更新的中期,之後x被更新,但y被更新之前,你最終會得到像素爲102的指針值,以及點py爲100

    1

    的情況非常清楚切 - 讀者可能不會看到更新直到某些東西觸發同步(互斥體,內存屏障,原子操作...)。許多事物過程確實隱含地觸發了這種同步 - 例如,外部函數調用(由於理由解釋了Usenet線程常見問題解答(http://www.lambdacs.com/cpt/FAQ.html) - 請參閱Dave Butenhof的回答需要易失性,所以如果您的代碼處理的值很小足夠的,他們不能被半寫(例如數字而不是字符串,固定地址而不是動態(重新)分配),那麼它可以在沒有顯式同步的情況下緩慢進行。

    如果你的表現想法是通過你的編寫代碼獲得更多的循環,那麼如果你忽略了同步,你會得到一個更好的數字。但是,如果您有興趣將平均和最差情況的延遲降至最低,以及讀者可以看到實際看到的多少更新,那麼您應該從作者那裏進行同步。

    +0

    感謝您的意見。您的評論推動了我的努力,並說服我實施MultiReader-SingleWriter鎖定我的變量。 – 2010-10-22 22:46:18

    相關問題