2013-07-03 69 views
0

這種情況總是頻繁發生:我們有一些線程和一個共享對象,我們需要確保在任何時候只有一個線程可以修改該對象。互斥守衛:有沒有對象的自動保護機制?

那麼,顯而易見的解決方案是使用lock the door-do the job-get out of there成語。在這種情況下,我總是使用POSIX互斥鎖。例如

pthread_mutex_lock(&this->messageRW);  // lock the door 
P_Message x = this->messageQueue.front(); // do the job 
this->messageQueue.pop(); 
pthread_mutex_unlock(&this->messageRW); // get out of there 
// somewhere else, in another thread 
while (true) { 
    P_Message message; 
    solver->listener->recvMessage(message); 
    pthread_mutex_lock(&(solver->messageRW));  // lock the door 
    solver->messageQueue.push(message);   // do the job 
    pthread_mutex_unlock(&(solver->messageRW)); // get out of there 
    sem_post(&solver->messageCount); 
} 

我在代碼這麼多的地方使用messageQueue。所以最後有很多不合適的鎖/解鎖對。我認爲應該有一種方法將messageQueue聲明爲應該在線程之間共享的對象,然後線程API可以處理鎖定/解鎖。我可以考慮一個包裝類,或類似的東西。儘管其他API(boost線程或其他庫)也是可接受的,但基於POSIX的解決方案更受歡迎。

你會在類似的情況下執行什麼?

更新未來的讀者

我發現this。我猜想會成爲C++ 14的一部分。

+4

爲什麼不創建一個包含互斥體和消息隊列的類,並公開用於訪問隊列的公共函數,並在這些公共函數中獲取並釋放該互斥體。這會從客戶端代碼中刪除獲取/釋放。 – hmjd

+2

檢查例如['的std :: lock_guard'](http://en.cppreference.com/w/cpp/thread/lock_guard)。 –

+0

@hmjd是的。這就是我提到的「包裝類方式」 –

回答

2

在這種情況下,您可以使用boost:scoped_lock。只要你走出去的範圍,它解鎖優雅

boost::mutex mMutex;//member mutex object defined somewhere 


{ //scope operator start 
    boost::mutex::scoped_lock scopedLock(mMutex); 
    pthread_mutex_lock();  // scoped lock the door 
    P_Message x = this->messageQueue.front(); // do the job 
    this->messageQueue.pop(); 
} //scope operator end, unlock mutex 

// somewhere else, in another thread 
while (true) { 
    P_Message message; 
    solver->listener->recvMessage(message); 
    boost::mutex::scoped_lock scopedLock(mMutex);  // scoped lock the door 
    solver->messageQueue.push(message);   // do the job 
    sem_post(&solver->messageCount); 
} //scope operator end, unlock mutex 
+0

如果範圍相同,該怎麼辦?例如,我們需要訪問範圍內的共享數據,執行一些其他人員,再次檢查數據。那麼我們有僵局嗎? –

+1

@soroush,無論您想要使用scope_lock,您都可以使用大括號{}進行作用域控制 –

2

爲此,我要麼子類(IS-A)或有(有-A)的消息隊列類到另一個類這就迫使使用互斥體。

這在功能上與其他語言一樣,例如Java​​關鍵字 - 它修改了要自動保護的底層對象。

1

這是消息隊列本身,它應該處理鎖定 (原子),而不是調用代碼。你需要的不僅僅是 一個互斥體,你還需要一個條件以避免競爭條件。 標準的成語會是這樣的:

class ScopedLock // You should already have this one anyway 
{ 
    pthread_mutex_t& myOwned; 
    ScopedLock(ScopedLock const&); 
    ScopedLock& operator=(ScopedLock const&); 
public: 
    ScopedLock(pthread_mutex_t& owned) 
     : myOwned(owned) 
    { 
     pthread_mutex_lock(&myOwned); 
    } 
    ~ScopedLock() 
    { 
     pthread_mutex_unlock(&myOwned); 
    } 
}; 

class MessageQueue 
{ 
    std::deque<Message> myQueue; 
    pthread_mutex_t myMutex; 
    pthread_cond_t myCond; 
public: 
    MessageQueue() 
    { 
     pthread_mutex_init(&myMutex); 
     pthread_cond_init(&myCond); 
    } 
    void push(Message const& message) 
    { 
     ScopedLock(myMutex); 
     myQueue.push_back(message); 
     pthread_cond_broadcast(&myCond); 
    } 

    Message pop() 
    { 
     ScopedLock(myMutex); 
     while (myQueue.empty()) { 
      pthread_cond_wait(&myCond, &myMutex); 
     } 
     Message results = myQueue.front(); 
     myQueue.pop_front(); 
     return results; 
    } 
}; 

這需要更多的錯誤處理,但其基本結構是 那裏。

當然,如果你可以使用C++ 11,你最好使用標準線程原語。 (否則,我通常會建議 Boost線程,但如果你已經使用Posix線程,你可能需要等待轉換,直到你可以使用標準的 線程,而不是轉換兩次。)但是你仍然需要 既是互斥體又是條件。

+0

OP正在使用信號量來管理隊列。 –

+0

@MartinJames這很難做到(而且他沒有發佈代碼,他在閱讀過程中同步它)。 –

+0

是的,但在sem_post中似乎沒有什麼意義,除非讀取正在執行sem_wait。 –