2010-02-15 49 views
6

當雙緩衝的數據這是由於被線程之間共享執行指針交換,我使用一個系統,其中一個線程從一個緩衝器讀出,一個線程從另一個緩衝器讀出和讀出從第一個緩衝區。麻煩的是,我將如何實現指針交換?我需要使用關鍵部分嗎?沒有互鎖功能可以實際交換數值。我不能讓線程從緩衝區讀取一個,然後開始從緩衝區讀取兩個,在讀取的過程中,這將是appcrash,即使其他線程沒有開始寫入。在雙緩衝的多線程系統

我在Visual Studio Ultimate 2010 RC的Windows上使用本機C++。

+0

你是什麼意思?「沒有互鎖功能可以實際交換值。」?爲什麼你不能使用這些功能? – AndiDog 2010-02-15 20:33:20

回答

1

爲什麼不能使用InterlockedExchangePointer

編輯:好吧,我給你說的話,現在,IEP實際上不換2點彼此生活的指針,因爲它只能通過引用,採用一個單值。

+0

如果您是InterlockedExchangePointer,則只能在呼叫中實際影響一個指針。這意味着爲了交換所有三個指標,我需要三個電話 - 在中間留下一個空白。 InterlockedExchangePointer實際上是一個用詞不當,它只會給給定值的給定指針。我預計它會交換一對指針(正是出於這個原因)。即使它的確如此工作,但如果不暫停這兩個線程來執行更新,我仍然無法實際使用它,這是我真正想避免的。 – DeadMG 2010-02-15 20:39:23

+0

@DeadMG:'buf1 = InterlockedExchangePointer(&buf2,buf1);'如果你從當前使用buf1的線程調用它,我認爲它會做你想做的。但我不完全確定你的意思是「所有三個指針」。 – Dan 2010-02-15 22:45:30

+0

我有一個來自渲染的緩衝區,一個來自遊戲邏輯的緩衝區(所以它們都可以讀取緩衝區),然後一個緩衝區以供遊戲邏輯寫入。問題是您調用的等價操作符不一定是原子 - InterlockedExchangePointer和buf1 =之間存在差距。即使沒有,在這種情況下,仍然沒有機制來確保我不在渲染過程中。 我認爲在另一個答案中描述的讀寫互斥解決方案可能是最好的。 – DeadMG 2010-02-15 23:05:20

6

使用關鍵部分是可以接受的方式。只需在你的指針操作/緩衝區讀/寫代碼周圍共享一個CRITICAL_SECTION對象,並在該對象上調用EnterCriticalSectionLeaveCriticalSection。嘗試儘快完成關鍵部分,儘可能在關鍵部分之外留下儘可能多的代碼。

即使你使用雙聯鎖交流技巧,你仍然需要一個臨界區或東西同步線程,所以還不如將其用於這個目的了。

+0

我可能會誤解OP的問題...... CRITICAL_SECTION不會導致序列化對緩衝區的所有訪問,從而破壞了其中兩個緩衝區的目的?我認爲這個想法是有兩個緩衝區,所以每個線程可以同時處理一些事情,偶爾緩衝區會被交換。 – Dan 2010-02-15 22:35:25

+0

那麼有2個緩衝區仍然有助於在某些情況下。例如,考慮雙緩衝交換鏈。 – Blindy 2010-02-15 23:11:42

+2

訪問數據時,您不會持有它,只能交換它們。 – DeadMG 2010-02-15 23:44:33

1

您必須構建自己的函數來交換使用信號量或臨界區的指針來控制它。需要將相同的保護添加到指針的所有用戶,因爲讀取正在被修改的指針的任何代碼是不好的

管理這個的一種方法是讓所有指針操作邏輯在鎖的保護下工作。

0

請參閱我最初設計的線程,以使它們完全異步,並且不需要在常規操作中進行任何同步。但是,因爲我在線程池中以每個對象爲基礎執行操作,所以如果給定的對象由於當前正在同步而不可讀,那麼在等待時我可以再做一次。從某種意義上說,我可以同時等待和操作,因爲我有很多線索可以解決。

創建兩個關鍵部分,每個線程一個。 渲染時,按住渲染暴擊部分。另一個線程仍然可以做到它喜歡的其他爆擊部分。使用TryEnterCriticalSection,如果它被保留,則返回false,並將該對象添加到列表中以稍後重新呈現。這應該允許我們繼續渲染,即使給定的對象當前正在更新。 更新時,同時保留兩個暴擊部分。 在進行遊戲邏輯的同時,保持遊戲邏輯暴擊部分。如果已經存在,那就沒有問題了,因爲我們的線程比實際的處理器多。所以如果這個線程被阻塞,那麼另一個線程將只使用CPU時間,這不需要管理。

+0

我不認爲你可以維持這種零同步方法的時間太長。你顯然已經在這裏遇到了一堵磚牆,並且在你的代碼中也可能存在一些bug,但是一般來說,更新遊戲狀態需要某種鎖定來使渲染狀態中間渲染不會失效。 – Blindy 2010-02-15 21:15:09

+0

什麼代碼? :P我現在只提供簡單的幾個文本對象來測試多線程設計。我真的不想寫一個渲染器,然後不得不返回並再次寫入多線程。 我會去看看我現在可以用鎖來完成什麼。 – DeadMG 2010-02-15 21:29:26

0

您還沒有提及您的Windows平臺限制,但是如果您不需要與Windows Server 2003或客戶端上的Vista相比較舊版本的兼容性,則可以使用InterlockedExchange64()函數來交換64位值。通過將兩個32位指針打包爲64位對結構,可以有效地交換兩個指針。

有通常的Interlocked * variantions; InterlockedExchangeAcquire64(),InterlockedCompareExchange64()等...

如果你需要運行,比如XP,我會去一個關鍵部分。當爭用的可能性很低時,它們表現相當好。

+0

我很樂意考慮不在XP上運行。但是,它並沒有解決交換中間渲染指針的問題。 – DeadMG 2010-02-15 21:24:40

4

這聽起來像是一個讀寫器互斥類型的問題。

    [...但我主要是做嵌入式開發,所以這可能沒有意義的Windows操作系統。 實際上,在基於優先級的調度程序的嵌入式操作系統中,如果您保證交換是原子性的並且只允許優先級較低的線程交換緩衝區,則可以根本不使用任何同步機制。 ]

假設您有兩個緩衝區B1和B2,並且您有兩個線程T1和T2。如果T1正在使用B1而T2正在使用B2,則可以。 「使用」是指讀取和/或寫入緩衝區。然後在某個時候,緩衝區需要交換,以便T1使用B2,而T2使用B1。你必須小心的是,交換是在兩個線程都不訪問緩衝區的情況下完成的。

假設您只使用了一個簡單的互斥鎖。 T1可以獲得互斥體並使用B1。如果T2想要使用B2,它必須等待互斥鎖。當T1完成時,T2將解除阻塞並與B2一起工作。如果任一線程(或某個第三方線程)想要交換緩衝區,則它也必須採用互斥鎖。因此,只使用一個互斥量序列化訪問緩衝區 - 不太好。

如果您改用讀寫器互斥鎖,它可能會更好。 T1可以獲得對互斥鎖的讀鎖,並使用B1。 T2也可以獲得對互斥鎖的讀鎖,並使用B2。當其中一個線程(或第三方線程)決定交換緩衝區時,它將不得不對互斥鎖執行寫入鎖定。直到沒有更多的讀鎖,它將無法獲得寫鎖。此時,它可以交換緩衝區指針,知道沒有人使用任何一個緩衝區,因爲當互斥鎖上存在寫入鎖定時,所有嘗試讀取鎖定的操作都將被阻止。

+0

一個有趣的解決方案 - 幾乎和我以前一樣,除了更清潔。但是,我無法在MSDN上找到關於這種互斥體的任何文檔。它們是WinAPI的固有組成部分,還是我必須自己編寫代碼? – DeadMG 2010-02-15 23:02:37

+0

打敗我。我谷歌'讀者作家互斥窗口',並發現這一點:http://msdn.microsoft.com/en-us/magazine/cc163599.aspx - 有幫助嗎?谷歌沒有'windows'來讀理論...... – Dan 2010-02-16 01:52:28

+0

如果傳入數據的順序很重要,那麼仍然需要同步。無論讀者/寫者互斥,閱讀者在繼續之前都必須等待新的數據到達。 – 2011-03-25 15:40:19