2017-10-18 102 views
0

我有一個表存儲一個像數據這樣的關鍵值,這個關鍵值將會被頻繁使用但很少更新。所以我想將必要的數據存儲在內存中,並且只在更新到來時才更新它。什麼是使用靜態地圖來緩存C++數據的推薦方式

下面是簡單的代碼顯示我目前的解決方案。

kv.h

class kv 
{ 
public: 
    string query(string key); 
    void update(string key, string value); 
}; 

kv.cpp

#include "kv.h" 
#include <map> 
#include <mutex> 
#include <thread> 

static map<string, string> s_cacheMap; 
static mutex mtx; 

string kv::query(string key) 
{ 
    unique_lock<mutex> lock(mtx); 
    if (s_cacheMap.empty()) 
    { 
     // load from db 
    } 

    auto it = s_cacheMap.find(key); 
    if (it != s_cacheMap.end()) 
    { 
     return (*it).second; 
    } 

    return ""; 
}; 

void kv::update(string key, string value) 
{ 
    unique_lock<mutex> lock(mtx); 
    s_cacheMap.clear(); 
    // write key value into db 
}; 

此溶液

的問題這些代碼將在iOS平臺庫的一部分由C++編寫。該應用程序可能隨時被系統或用戶殺死。我可以在應用程序退出時收到通知,但在用戶終止應用程序之前,我只有很短的時間才能清理。我無法保證這些線程在應用程序終止時仍然運行得到正確的結果,但我想確保它不會崩潰。

在應用程序生命週期結束時,這兩個靜態變量將被銷燬。當這兩個靜態變量被銷燬時,另一個線程嘗試調用這兩個方法,它將失敗。

可能的解決方案

1 - 裹靜態成方法等that

map<string, string>& getCacheMap() 
{ 
    static map<string, string> *s_cacheMap = new map<string, string>; 
    return *s_cacheMap; 
} 

2 - 使KV類如單

static kv& getInstance() 
{ 
    static kv* s_kv = new kv(); 
    return *s_kv; 
} 

問題

除了這兩個解決方案之外,還有其他解決方案嗎?

+1

在所有線程完成之前不要退出程序? – davidbak

+0

我同意來自'davidbak'的上述評論,你應該確保所有的線程在破壞上述靜態變量被銷燬之前完成。這更多的是通過堆棧調用操作符的順序問題,而不是有效使用靜態映射或緩存數據。 –

+0

@davidbak我在C++編寫的iOS平臺上提供了一個庫。所以這個應用程序可能隨時被系統或用戶殺死。我可以在應用程序退出時收到通知,但在用戶終止應用程序之前,我只有很短的時間才能清理。我無法保證這些線程在應用程序終止時仍然運行得到正確的結果,但我想確保它不會崩潰。 – 2power10

回答

1

當這兩個靜態變量已經被銷燬時,另一個線程試試 來調用這兩個方法,它會失敗。

你真正的問題在於你仍然有在main()的末尾運行的線程。這不好;即使你解決了這個問題,你仍然會在關機時遇到其他(類似)競爭情況,其中一些情況你將無法解決。

正確的修復方法是確保所有衍生線程都已退出並保證在您對其可能訪問的資源進行任何清理之前(例如,在main()返回之前)。特別是,你需要告訴每個線程退出(例如,通過設置一個std::atomic<bool>或類似的線程定期檢查,或者關閉線程正在監視的套接字,或者通過任何其他可以提供的跨線程通知機制) ,然後讓主線程調用線程對象的join(),以便主線程將在join()內部阻塞,直到子線程退出。

一旦你做到了這一點,就會關閉期間沒有更多的競爭條件,因爲不會有螺紋留下不適當嘗試訪問被刪除的資源。

+0

你的建議很好,但前提是我完全瞭解應用程序的生命週期。我在iOS平臺上提供了一個庫,這個應用程序可能在任何時候都被用戶或系統殺死,並且只剩下有限的時間等待所有線程退出。 – 2power10

+0

你可以添加一個MyLibrary_Shutdown()調用到你的庫的API並要求主機程序在退出之前調用該函數嗎?這將處理控制退出案件;在不受控制的退出/強制退出的情況下,你可以做的事情不多。 (或者如果問題是你認爲你的線程一旦通知就會花費太長時間才能退出,那麼解決方案就是確保你的線程快速退出;例如,通過使用事件驅動的通知機制而不是定期輪詢) –

0

使用間接 - 解決所有編程的問題。

創建的接口類的數據結構 - 在這種情況下兩種方法,queryupdate - 在所有的方法都是純虛

聲明靜態爲指向此接口類型的指針。

創建兩個實現子類:一個是真實的,其他什麼也不做(但返回默認值在必要時)。

在應用程序啓動時創建一個真實的實例,把它貼在靜態指針。在應用程序退出時,創建一個無所事事的實例,將其交換爲靜態指針,並刪除靜態指針中的實例。 (還是不刪除,如果應用程序/過程實際上是要離開。)

由於本地圖正在更新它顯然已經有一個全局鎖(或讀寫鎖)。交換指針操作也需要使用該鎖,以確保在交換數據結構時沒有人處於數據結構中。但是鎖需要移動到數據結構的指針。最簡單的方法是有一個接口的第三個第三個子類,它包含一個指向數據結構的指針(前一個'靜態指針'),並在採取適當的鎖定之後將所有操作轉發給包含的實例。

(這聽起來很複雜,但事實並非如此,我自己也是在我們不得不將DLL加載到操作系統網絡堆棧的情況下自己完成的,在這種情況下,它將停留在不能卸載的地方直到操作系統重新啓動,但是當升級應用程序時需要升級DLL的實現,這個時間發生的時間不需要重新啓動操作系統,我提供了一個可以加載到操作系統的整個轉發DLL ,並且加載/卸載/重新加載實際 DLL做了工作,將所有操作轉發給它,以及跟蹤何時更舊的 DLL不再被使用(所有操作返回)並且可以被釋放。 )

替代,除了忠實地偏執不必要的:在什麼都不做的情況下,可以聲明爲static過,那麼你只要把它的指針到靜態指針到界面的應用程序退出。它不需要清理(刪除)。

你知道,如果這是一個應用程序生命週期的事情,並且該過程被破壞掉了,無論如何,爲什麼不收拾這個靜態地圖呢?

+0

沒有一些指針交換技術會受到競爭條件的影響 - 例如,當主線程獲得CPU時,子線程可能已經讀取了「實例」指針並準備使用它,將指針交換出去,並釋放共享資源。然後幾毫秒後,子線程再次運行,嘗試使用(現在無效的)實實例指針並使進程崩潰。 –

+0

@JeremyFriesner - 真的,我會更新答案。 – davidbak

相關問題