2016-09-30 41 views
1

我們已經有了一個應用程序,用於處理與線程池傳入的數據包。每個線程都有一個正在使用數據包處理的配置。快速路徑數據包處理的配置更新

我們目前正在使用互斥鎖來檢查配置是否已更改。

這使線程花費太多時間來鎖定互斥鎖,以檢查是否存在配置更新。我們想知道是否有更快的選擇你們可以建議。 實現與C++

關心。

+0

是每個線程只處理單個數據包,或數據包流?你需要檢查每個數據包的變化嗎?如果每個線程在開始處理時都獲得了配置的靜態副本,這不是更容易嗎?這是可行的嗎? –

+0

如果存在對config的大量只讀訪問模式,但相對不頻繁的寫訪問(更新配置),則使用讀寫器鎖而不是互斥鎖 –

+0

@JoachimPileborg每個線程正在處理數據包並檢查如果配置發生變化,則不可行,因爲配置隨機更改 – xpath

回答

1

解決此問題的一種可能方法是通過atomics via std::atomic。以下是簡化版問題的簡化解決方案。在下文中,您的問題已簡化爲單個處理器線程(原則上多個情況相同)。該解決方案的第一個版本在配置更改中「泄漏」。對於罕見的配置更改(至少從我的經驗來看,這是一個非常常見的情況),這可能是可以接受的。否則,我將在最後描述兩種解決方法。

假設您從以下配置類:

#include <thread> 
#include <vector> 
#include <list> 
#include <iostream> 
#include <atomic> 
#include <chrono> 

constexpr int init_config_val = 3; 

struct config{ 
    int m_val = init_config_val; 
}; 

配置有單值字段,m_val

現在讓我們設置類型的原子指針的配置,並配置列表:

using config_atomic_ptr_t = std::atomic<config *>; 
using config_list_t = std::list<config>; 

線程過程需要一個指向原子結構的指針。當它需要訪問配置時,它會調用std::atomic::load

void process(config_atomic_ptr_t *conf) { 
    while(true) { 
     const config *current_config = conf->load(); 
     ... 
    } 
} 

(請注意,上面顯示的線程檢查在每次迭代時的配置;在某些類型的應用程序,它可能足以檢查「往往不夠」。)

在不同的線程希望設置配置時,它調用下面的函數:

void modify_config(config_list_t &configs, config_atomic_ptr_t &current_config, config conf) { 
    configs.push_back(conf); 
    current_config.store(&*configs.rbegin()); 
} 

該函數接受要配置的列表,涉及一種原子結構指針的引用,並且一個新的配置對象的引用。它將配置對象推送到列表的末尾,然後使用std::atomic::store將指針設置爲列表中的結束元素。

這是怎麼main可以設置的東西:

int main() { 
    config_list_t configs; 
    configs.push_back(config{});  
    config_atomic_ptr_t current_config{&*configs.rbegin()}; 

    std::thread processor(process, &current_config); 
    config new_conf{init_config_val + 1}; 
    modify_config(configs, current_config, new_conf); 
    processor.join(); 
} 

如前所述,每個配置更改推一個新的配置對象的名單,因此該方案實際上具有無限的內存需求。

至少從我的經驗來看,許多應用程序在原則上需要支持配置更改,但預計它們很少見。如果是這樣,上述解決方案可能是可以接受的。 (實際上,您可以通過刪除列表來簡化操作,只需在堆上分配新的配置。)

如果不是,則至少有兩種選擇。

第一替代方法包括固定上述如下:

  • config,添加描述的配置版本的另一個字段 - 說,一個整數。
  • 發送process線程還指向std::atomic<int>
  • 定期(例如每1000次迭代一次),線程將檢查它正在使用的配置的版本,並設置std::atomic<int>以反映它。
  • 清理線程(可能是主線程)也會定期檢查std::atomic<int>的值,並相應地清理列表。

第二種方法是將線程函數傳遞給boost::lockfree::queue之類的指針。在每次迭代中(或者每次迭代一次),線程可以檢查隊列中的新配置,然後使用它。


全部實施例

#include <thread> 
#include <vector> 
#include <list> 
#include <iostream> 
#include <atomic> 
#include <chrono> 

constexpr int init_config_val = 3; 

struct config{ 
    int m_val = init_config_val; 
}; 

using config_atomic_ptr_t = std::atomic<config *>; 
using config_list_t = std::list<config>; 

void process(config_atomic_ptr_t *conf) { 
    while(true) { 
     const config *current_config = conf->load(); 
     if(current_config->m_val != init_config_val) 
      break; 
    } 
} 

void modify_config(config_list_t &configs, config_atomic_ptr_t &current_config, config conf) { 
    configs.push_back(conf); 
    current_config.store(&*configs.rbegin()); 
} 

int main() { 
    using namespace std::chrono_literals; 

    config_list_t configs; 
    configs.push_back(config{});  
    config_atomic_ptr_t current_config{&*configs.rbegin()}; 

    std::thread processor(process, &current_config); 
    std::this_thread::sleep_for(1s); 
    config new_conf{init_config_val + 1}; 
    modify_config(configs, current_config, new_conf); 
    processor.join(); 
}