2009-12-23 57 views
0

我遇到以下問題。如何添加一個項目到兩個隊列並保證它存在於兩者之中(多線程)

我有兩個類,在這種情況下,A和B,都擁有concurrent_queue。這裏的假設是concurrent_queue是一個線程安全隊列,具有阻塞push()函數。當一個對象在B中入隊時,它訪問單例A並且它也在A中排隊。這樣做的結果是一大堆B的隊列與他們自己的對象沒什麼關係,而A中的一個大隊列包含了他們。 B的每個實例都可以存在於一個單獨的線程中。

我遇到的情況是,線程在B :: foo()中的兩行代碼之間經常被搶佔,這意味着A :: mQueue包含該對象,但B :: mQueue尚未包含物體。

我想知道的是如何確保當調用B :: foo()時,該對象要麼被推到兩個隊列中,要麼都不推送到隊列中。在我看來,我必須在A中擁有一個互斥體,B才能獲得並鎖定A的互斥體到B :: foo()中。

有沒有人有任何建議,我怎麼能做到這一點,或者我如何重組我的代碼來完成這個?我正在使用boost ::線程庫。

Class A 
{  
public: 
    A& instance(){/* return singleton */}   
    void addToQueue(SomeObject const& obj) 
    { 
     mQueue.push(obj); 
    }   
private: 
    concurrent_queue<SomeObject> mQueue; 
}; 

Class B 
{ 
public: 
    void foo() 
    { 
     SomeObject obj; 
     //I would like to guarantee that obj is either present in both queues or neither queue 
     A::instance().addToQueue(obj); 
     mQueue.push(obj); 
    }   
private: 
    concurrent_queue<SomeObject> mQueue; 
}; 

在我的實際應用中,它不是被在A和B排隊相同的對象,而將A所有排隊包含指向B的結構,這讓我離隊一切都在A和出隊B的順序與排隊時相同,但這與問題無關。

回答

2

您需要將「將對象添加到兩個隊列」的操作原型化。你需要在你的兩個函數調用周圍有一個鎖或其他類型的同步原語。刪除隊列中的項目也是一樣。

boost::mutex看起來適合這份工作。您需要一個實例,並且需要從隊列修改的任何位置訪問它。因爲它也將有相同的壽命爲A的隊列中,我建議你把它放在A,然後修改隊列存取,因此看起來像是:

A::instance().lockQueue(); //calls A.mQueueAccessMutex.lock(), probably 
    A::instance().addToQueue(obj); 
    mQueue.push(obj); 
A::instance().unlockQueue(); 

或者,RAII風格:

{ 
    LockHolder lh(A::instance().getLock()); //lock called in lh's constructor 

    A::instance().addToQueue(obj); 
    mQueue.push(obj); 

    //unlock called in lh's destructor 
} 

注那麼concurrent_queue將是多餘的,因爲沒有兩個線程將同時訪問隊列。

-

,當然,總有那麼簡單地顛倒你把隊列中的項目的順序將解決你的問題的機率很小。 :)

1

您可能確實需要某種形式的互斥體才能保證原子性(相對於其他應用程序而言)。 Boost :: threading確實提供互斥對象iirc,因此您可能需要查看該對象。

0

據我瞭解B:foo應該保證在兩個隊列中都加入對象,但後來對這些隊列的訪問應該是獨立的。

在這種情況下,您應該使用某種方法來增強A,直接鎖定隊列或返回此隊列中使用的互斥鎖(我假設您的concurent_queue是基於互斥鎖的)。之後,b :: foo應該先鎖定這兩個互斥鎖,然後再推,釋放這兩個互斥鎖。

不要忘記處理異常,f.e.如果失敗添加到第二個隊列應該從第一個刪除。

相關問題