我們已經有了一個應用程序,用於處理與線程池傳入的數據包。每個線程都有一個正在使用數據包處理的配置。快速路徑數據包處理的配置更新
我們目前正在使用互斥鎖來檢查配置是否已更改。
這使線程花費太多時間來鎖定互斥鎖,以檢查是否存在配置更新。我們想知道是否有更快的選擇你們可以建議。 實現與C++
關心。
我們已經有了一個應用程序,用於處理與線程池傳入的數據包。每個線程都有一個正在使用數據包處理的配置。快速路徑數據包處理的配置更新
我們目前正在使用互斥鎖來檢查配置是否已更改。
這使線程花費太多時間來鎖定互斥鎖,以檢查是否存在配置更新。我們想知道是否有更快的選擇你們可以建議。 實現與C++
關心。
解決此問題的一種可能方法是通過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 ¤t_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, ¤t_config);
config new_conf{init_config_val + 1};
modify_config(configs, current_config, new_conf);
processor.join();
}
如前所述,每個配置更改推一個新的配置對象的名單,因此該方案實際上具有無限的內存需求。
至少從我的經驗來看,許多應用程序在原則上需要支持配置更改,但預計它們很少見。如果是這樣,上述解決方案可能是可以接受的。 (實際上,您可以通過刪除列表來簡化操作,只需在堆上分配新的配置。)
如果不是,則至少有兩種選擇。
第一替代方法包括固定上述如下:
config
,添加描述的配置版本的另一個字段 - 說,一個整數。process
線程還指向std::atomic<int>
。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 ¤t_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, ¤t_config);
std::this_thread::sleep_for(1s);
config new_conf{init_config_val + 1};
modify_config(configs, current_config, new_conf);
processor.join();
}
是每個線程只處理單個數據包,或數據包流?你需要檢查每個數據包的變化嗎?如果每個線程在開始處理時都獲得了配置的靜態副本,這不是更容易嗎?這是可行的嗎? –
如果存在對config的大量只讀訪問模式,但相對不頻繁的寫訪問(更新配置),則使用讀寫器鎖而不是互斥鎖 –
@JoachimPileborg每個線程正在處理數據包並檢查如果配置發生變化,則不可行,因爲配置隨機更改 – xpath