簡體   English   中英

多線程鏈表遍歷

[英]Multithreaded linked list traversal

給定(雙重)鏈接的對象列表(C ++),我有一個我想要多線程的操作,以對每個對象執行。 每個對象的操作成本不均勻。 由於各種原因,鏈表是這組對象的首選存儲。 每個對象中的第一個元素是指向下一個對象的指針; 第二個元素是列表中的上一個對象。

我通過構建節點數組並應用OpenMP解決了這個問題。 這給了不錯的表現。 然后我切換到我自己的線程例程(基於Windows原語)並使用InterlockedIncrement()(作用於數組中的索引),我可以實現更高的整體CPU利用率和更快的吞吐量。 從本質上講,線程沿着元素“跳躍式”運行。

我的下一個優化方法是嘗試消除創建/重用鏈表中的元素數組。 但是,我想繼續這種“跳躍式”方法並以某種方式使用一些不存在的例程,可以稱為“InterlockedCompareDereference” - 以原子方式比較NULL(列表結尾)和有條件地解除引用和存儲,返回解除引用的值。

我不認為InterlockedCompareExchangePointer()會工作,因為我無法原子地取消引用指針並調用此Interlocked()方法。 我做了一些閱讀,其他人正在建議關鍵部分或自旋鎖。 關鍵部分在這里似乎很重要。 我很想嘗試旋轉鎖,但我想我首先在這里提出問題並詢問其他人在做什么。 我不相信InterlockedCompareExchangePointer()方法本身可以像旋轉鎖一樣使用。 然后還必須考慮獲取/發布/圍欄語義......

想法? 謝謝!

關鍵部分的重量不是很重。 假設他們可以快速獲得鎖,他們就像旋轉鎖一樣。

您的問題的解決方案將取決於您是否正在修改列表。 如果你沒有修改列表,你需要做的就是將InterlockedCompareExchange對象中的值初始化為0.交換值為1,如果你得到0,那么你做你的行動,如果你得到1回,你跳過 下次在列表上執行操作時,您將在2中交換並檢查1/2而不是0/1。

如果您要更改列表,那么一切都取決於。 如果你只想向前移動,並且只刪除當前節點,那么最好的選擇是在執行比較交換位,跳躍(獲取它的下一個節點)和刪除時鎖定的項目中有下一個鎖定節點。

我認為卡斯塔有一些好處,我會自己撿到一個。 這個問題的解決方案將嚴重影響在每個節點上完成的思想變換,無論它們是否相互依賴,以及完成的任務是否可以在列表的單個掃描中完成。

如果操作不相互依賴,並且掃描方法有意義,我建議使用代理提取系統。 它實際上與工作人員方法相同。 每個線程都交給一個管理列表的代理的引用,因為每個線程需要內容來處理它,它從代理請求它,它鎖定,獲取下一個節點,推進內部掃描迭代器,並釋放鎖,返回節點到請求線程。 這將繼續,直到列表枚舉完成。 對於累積器問題,其中每個節點可能被不同的線程多次訪問,您可以使用循環列表或其他一些此類容器。 一個有天賦的經紀人可以管理列表,包括新的插入,刪除等,與調度相同:鎖定,操作,解鎖。 顯然,很多活動可以根據您在經紀方的具體需求進行定制。 這些需求可以構成一個精心設計的池管理系統,同時在鎖定爭用方面仍然非常有效(即幾乎沒有)。

然而,底線有望顯而易見。 了解您的問題集以及您希望每個線程使用其當前節點完成的具體細節。

基本上,要盡快瀏覽列表,您需要避免一些事情;

  • 鎖定碰撞。 即使使用自旋鎖,每次循環迭代都錯過了完成工作的機會。
  • 記憶障礙。 每次執行原子操作時,內存屏障都會暫停執行。
  • 不必要的工作,例如掃描工作列表。

我必須同意你的閱讀並采取自旋鎖的一面。

您將指針放在易失性指針中的列表頭部。

然后每個線程輪流;

  • 采取旋轉鎖定
  • 將指針的值保存在臨時值中
  • 更新指向下一個列表條目的指針
  • 釋放自旋鎖。

然后它可以開始處理臨時指向的條目。

使用每個條目鎖來查找列表有一些優點;

  • 如果每個項目的工作需要非常重要的時間來完成,那么在更新指針的短暫持續時間內,您將只有很少的沖突。
  • 除非發生碰撞,否則每個列表項只會有2個內存屏障,一個用於鎖定,一個用於解鎖。
  • 沒有掃描列表來獲取新的工作項,只需從“隊列”中獲取工作。

由於對齊,列表指針的低位包含零。 您可以通過使用比較和交換指令將對象標記為正在處理來原子地設置其中一個指針中的一個位來利用它。

每個線程都會這樣做:

  1. 從頭開始遍歷列表。
  2. 對於每個列表節點,檢查其下一個指針是否設置了較低位。
  3. 如果該位設置為goto 7。
  4. 比較並交換列表節點的下一個指針(next | 1)
  5. 如果比較和交換失敗轉到7。
  6. 處理對象。
  7. 移動到下一個節點並轉到2。

另一種選擇是將此位標記放入一個在對象本身中具有未使用位的整數,除非列表節點是基類或對象的成員。

這樣線程就可以從列表中獲取對象,而不會以無等待的方式阻塞或忙於旋轉。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM