2012-02-15 135 views
0

一個類的XYZ有3個成員變量x,y,z。有[N]個XYZ對象的向量。多線程,線程間通信,同步

有3個線程A,B,C,它們可以訪問vector中的任何對象和該對象的任何成員變量。

class XYZ 
{ 
public: 
double x; 
double y; 
double z; 
}; 

std ::具有N個元素的向量,其中N在整個程序中是固定的。

如何設計線程間通信以實現線程安全和最高效率,即最小的阻塞。

這裏是我的一些思考過程,如果我錯了,請糾正我

  1. 將向量除以更小的向量並封裝到每個線程中 類,然後使用消息隊列傳遞數據。問題 都是3個線程都可以訪問矢量和對象 成員的任何地方,因此很難細分和封裝。 消息隊列本身需要阻塞,即當發件人添加到隊列時,讀取器需要被阻塞 。
  2. 使用原子庫使訪問原子,因此避免 阻塞。問題原子依賴於操作系統,即某些操作被認爲是原子操作,在Linux下可能不是原子操作,在Windows下可能不是原子操作。
  3. 互斥,爲每個成員變量添加3個互斥對象,例如mutex_x, mutex_y,mutex_z。然而,問題是互斥的不可複製,

也就是說,如果我們有一個類一樣,

class XYZ_mutex 
{ 
public: 
double x; 
double y; 
double z; 
boost::mutex mutex_x; 
boost::mutex mutex_y; 
boost::mutex mutex_z; 
}; 

我們不能有XYZ_mutex的載體,因爲.push_back()是一個拷貝構造函數。

謝謝。

+3

爲什麼要使用三個互斥量而不是一個互斥量?而你關於沒有矢量的邏輯是錯誤的 - 你可以爲你的類創建一個拷貝構造函數(當然,它不會複製互斥量,但你爲什麼要這樣做?) – 2012-02-15 15:45:50

+0

雖然「我們不能一個XYZ_mutex矢量,因爲.push_back()是一個拷貝構造函數。「是真的,你可以擁有'ZYX_mutex *'的矢量。 – Griwes 2012-02-15 15:50:29

+0

線程A,B,C對向量做了什麼? – 2012-02-15 15:55:12

回答

2

您需要考慮您的程序具有哪些使用模式和一致性要求。

第一個也是最重要的使用模式是任何線程是否需要修改這些結構。如果沒有,你實際上不需要任何鎖定 - 只要確保在線程開始讀取之前填充結構。如果線程不得不修改結構,則需要考慮一致性。你需要問自己,如果有任何限制,除了修改個人double值,該限制,當一個線程可以看到另一個線程所做的更改。

一旦你定義了這一點,就開始思考數據是否可以以任何方式在線程之間拆分,以減少線程之間的衝突 - 即使它不能完全消除,問題的答案是保留三個向量的雙倍更好,一個XYZ矢量或其他組織的依賴關係,是否會有一些線程更頻繁地訪問某些XYZ對象,或者是否會有線程更頻繁地訪問x成員,而其他線程則更頻繁地訪問ys或zs等。

如果你不能說任何線程訪問任何實例的任何成員的概率,很難說如何最好地組織它們。將它們放在堆上以便它們落入不同的緩存行中可能是個好主意。總的來說,最好的建議可能是將它們放入任何最適合您的需求的數據結構(矢量,地圖,集合等),使用單個互斥體同步整個事物,編寫程序和然後測試是否遇到瓶頸。

1

我會選擇1(有一些變化,請參閱下文),只是因爲數組中的每個項的互斥體代價太大。

因此,將您的數組分成更小的塊,每個塊都受互斥鎖保護是一個不錯的主意。然後,當線程需要訪問數組的某個部分時,它可以訪問另一個數據結構,該數據結構根據訪問的項目所在的時間間隔爲其提供需要鎖定的互斥鎖(例如,將索引關聯到索引具有該特定時間間隔的互斥項目)。

+0

如果他能隔離訪問,這可能是一個好主意。然而,如果一個線程需要訪問一個部分以更新另一個部分,它將需要獲得兩個鎖,並且避免死鎖是棘手的(儘管可以完成)。 – 2012-02-15 16:03:25

1

沒有一個正確的答案。其基本規則是:

  1. 試圖組織的東西所以沒有線程每一個需要多於一個 互斥。否則,您需要一些(通常不明顯的)解決方案來避免死鎖的風險。

  2. 儘量保持每個互斥量儘可能短。如果你可以組織事物,以便有n個不同的數組,每個線程只有 訪問一個不同的數組,每個不同的數組只有一個類訪問 ,這是最佳的,因爲這意味着每個線程都可以採取行動一旦它獲得了其不同陣列的所有權,就無需鎖定。 (它聽起來不像是這種情況,但是。)

在很大程度上將取決於線程的方式(和頻率)訪問向量的 元件。

最後,有幾種處理 ,XYZ對象中互斥量問題的方法。最明顯的是使用最近編譯器,其中 支持移動語義; std::mutex是可移動的,並且可以在 std::vector中使用。如果不這樣做,你可以使用boost::shared_ptr<>到 互斥量。

+0

謝謝James,經過幾天的研究,我找到了使用boost :: shared_ptr的方法。這是一篇相當不錯的文章。 http://www.boost.org/doc/libs/1_48_0/libs/smart_ptr/sp_techniques.html – 2607 2012-02-21 04:23:06

0

使用選項1.但也將矢量分成3部分,並允許每個線程在其塊上工作,以不干擾其他線程。您始終可以使用矢量的長度來創建這些虛擬邊界。哎呀,你可以讓每個線程都有自己的向量。然後,當你插入到矢量中時,通過一個「負載平衡器」發送你的請求,使它插入到正確的矢量中(只是貪婪)。您也可以使用平衡器重新平衡向量,因爲某個線程比其他線程慢(將項目從1個向量移動到其他線程)