2013-08-02 90 views
0

安全類實現我已經有2件類:線程兩臺服務器

RequestController { 
    public: 
    SendRequest(); // called by multiple threads synchronously 
    private: 
    server primary; // This is the primary server 
    server backup; // This is the back up server. 
} 

我的邏輯很簡單:

在sendRequest將(),我想將請求發送到主服務器, 如果失敗,我想把它發送到備份服務器,如果通過的話, 我想交換主服務器和備份服務器。

問題出現了:當我進行交換時,我必須鎖定主備份和備份(這是多個線程不能同時執行的地方)。 其實我需要確保當我交換時,沒有線程正在讀主服務器。 如何以有效的方式編寫這段代碼? 我不想鎖定整個事情,因爲大多數 的情況下,主服務器工作,並且不需要鎖定。

我認爲這個問題通常是語言無關的。無論如何,我用C++標記這個 。

+0

如果您使用語言或系統或特定內容標記此標記,您可能會獲得更多幫助。 –

回答

0

讓我們假設該服務器採取的一些時間不可忽略量的處理請求。然後,如果請求速度足夠快,則會在等待其中一個服務器處理先前請求時調用SendRequest

作爲一個設計師,你有兩個選擇。

  1. 如果服務器可以同時處理多個請求,那麼你什麼也不做。
  2. 如果一個服務器只能一次處理一個請求,那麼你就需要在代碼中執行某種同步。

在情況2中,由於您已經在服務器上有鎖,因此您可以交換它們而不產生分支。

對於案例1,爲什麼不能做到以下幾點:

std::mutex my_mutex; 
... 
// Select the server 
server* selected = NULL; 
my_mutex.lock(); 
    selected = &primary; 
my_mutex.unlock(); 

// Let the selected server process the message. 
bool success = selected->process(); 

// If there was a primary failure, see if we can try the backup. 
if (!success) { 
    my_mutex.lock(); 
    if (selected == &primary) { 
    selected = &backup; 
    } 
    my_mutex.unlock(); 

    // Now try again 
    success = selected->process(); 

    // If the backup was used successfully, swap the primary and backup. 
    if (success) { 
    my_mutex.lock(); 
    if (selected == &backup) { 
     backup = primary; 
     primary = selected; 
    } 
    my_mutex.unlock();  
    } 
} 

但是這可能有一些問題。比如說,第一條信息主要失敗,但其他信息成功。如果sendRequest將()由3個不同的線程調用在同一時間,則可以有以下幾種:

  • 線索1 - 發送具有初級
  • 線程2 - 原發性
  • 線程3發送 - 發送與伯
  • 線程1 - 失敗,發送具有備份
  • 線程2 - 初級成功
  • 線程1 - 備份成功
  • 線程1 - 互換主和備份
  • 線程3 - 舊的主(新的備份)成功
  • 線程3 - 交換主用和備用

如果消息不斷傳來速度不夠快,有可能保持這種狀態,你保持交換主要和備份。該條件可以解決沒有掛起消息的時刻,然後主設備和備份設置將被設置,直到出現另一個故障。

也許一個更好的辦法是永不掉,但有一個更好的選擇方法。例如:

... 
// Select the server 
server* selected = NULL; 
selected = &primary; 
if (!primary.last_message_successful) { 
    // The most recent attempt made with primary was a failure. 
    if (backup.last_message_successful) { 
    // The backup is thought to be functioning. 
    selected = &backup; 
    } 
} 

// Let the selected server process the message. 
// If successful, process() will set the last_message_successful boolean. 
bool success = selected->process(); 

// If there was a failure, try the other one. 
if (!success) { 
    if (&primary == selected) { 
    selected = &backup; 
    } else { 
    selected = &primary; 
    } 
} 

// Try again with the other one. 
selected->process(); 

在本例中,鎖是沒有必要的。主要將被使用,直到它失敗。然後將使用備份。如果同時處理其他消息,則可能會導致主服務器再次可用,在這種情況下,它將被使用。否則,將使用備份直到失敗。如果兩者都失敗了,它們都會被嘗試,首先是主要的,然後是備份。

+0

對我而言,1是正確的。服務器可以同時處理多個請求。 – WhatABeautifulWorld

+0

雖然沒有線程可能正在讀取主服務器,但我看不出如何滿足他的要求。在調用進程()期間,交換時使用的鎖定不被保持。 –

+0

@Joe Runde正如OP剛纔所說的,在調用process()過程中不需要保持鎖,因爲服務器能夠同時處理多個請求。 – Trenin