2009-03-05 8 views
7

私は結果をキャッシュしたい、比較的高価なデータフェッチ操作を持っています。キャッシュ高価なデータ - 変更可能なメンバ変数VS機能スコープ静

double AdjustData(double d, int key) const { 
    double factor = LongRunningOperationToFetchFactor(key); 
    return factor * d; 
} 

私はconstを維持するAdjustDataのようにしたいが、私はそう私はそれを最初にフェッチ要因をキャッシュする:この操作は、おおよそ次のように、constメソッドから呼び出されます。現在はを使用して結果を保存しています(マップはkeyからfactorまでです)。しかし、私は関数スコープの静的関数を使用する方が良い解決策であると考えています。この要素はこの関数でのみ必要です。クラスの残りの部分とは無関係です。

これは良い方法のようですか?より良い選択肢はありますか?特に糸安全性に関して、私はどのようなことを考えていますか?

おかげで、

ドム

答えて

5

LongRunningOperationToFetchFactorの実装を次のようにラップします。 Boostスコープロックを使用していますが、他のロックフレームワークと似たようなことができます。

#include <boost/thread/thread.hpp> 
#include <boost/thread/mutex.hpp> 
#include <map> 

using namespace std; 

static boost::mutex myMutex; 
static map<int,double> results; 

double CachedLongRunningOperationToFetchFactor(int key) 
{ 

    { 
     boost::mutex::scoped_lock lock(myMutex); 

     map<int,double>::iterator iter = results.find(key); 
     if (iter != results.end()) 
     { 
      return (*iter).second; 
     } 
    } 
    // not in the Cache calculate it 
    result = LongRunningOperationToFetchFactor(key); 
    { 
     // we need to lock the map again 
     boost::mutex::scoped_lock lock(myMutex); 
     // it could be that another thread already calculated the result but 
     // map assignment does not care. 
     results[key] = result; 
    } 
    return result; 
} 

これが実際に長時間実行される操作の場合、ミューテックスをロックするコストは最小限に抑える必要があります。

あなたの質問からはっきりしませんでしたが、関数LongRunningOperationToFetchFactorがあなたのクラスのメンバ関数であれば、同じクラスのマップに変更可能なマップが必要です。私はアクセスのための単一静的ミューテックスはまだ十分に速いです。

+0

長時間実行されている操作をロックする必要はなく、マップ上で呼び出しを検索/挿入するだけで済みます。 –

+0

私はあなたが正しいと思います。私はさらにそれを調整してみましょう。 –

+0

ロックを保持していない静的マップの作成は、スレッドセーフであることが保証されていません。なぜなら、2つのスレッドが初めてこの関数を同時に呼び出すと、マップを二重構築して二重構造にするからです。 http://blogs.msdn.com/oldnewthing/archive/2004/03/08/85901.aspx – bk1e

0

私は理解していない場合を除き、それはあなたがこれを静的にしたいと私には明らかなようだ:

double AdjustData(double d) const { 
    static const double kAdjustFactor = LongRunningOperationToFetchFactor(); 
    return kAdjustFactor * d; 
} 

そのように、あなたが唯一の要因を取得します一度。

+0

こんにちはリンドゼイ - ありがとう、それは私が考えていたものです。私は私の質問を編集して実際の問題を洗い出しました。私は静的なconstを使用することはできませんので、私は静的なマップを使用し、マップの挿入の周りにいくつかの種類のロックを行うと思います。それは正しいようですか? –

+0

あなたが話していることはうまくいくでしょうが、解決策にはやや悪い臭いがあります:)(リファクタリングの本から取った)。これは、ユーザーが別のスレッドで実行して結果が表示されたときに実行できるさまざまな「キー」に対してユーザーが開始するものですか? –

+0

静的にすると、クラスのすべてのオブジェクトが値を共有します。これは、あなたの望むことですか? –

1

singleton pattern(1)は、長期実行操作を実行し、結果をキャッシュするクラスで使用できます。このインスタンスは、他のクラスのconstメンバ関数で使用できます。スレッドの安全性のために、挿入データと抽出データをマップデータ構造から保護するための相互排除を検討してください。マルチスレッドのパフォーマンスが大きな問題である場合、複数のスレッドが同じキーを同時に計算しないように、進行中のキーにフラグを立てることができます。

#include <cstdlib> 
#include <iostream> 
#include <map> 

using namespace std; 

class FactorMaker { 
    map<int, double> cache; 

    double longRunningFetch(int key) 
    { 
     const double factor = static_cast<double> (rand())/RAND_MAX; 
     cout << "calculating factor for key " << key << endl; 
     // lock 
     cache.insert(make_pair(key, factor)); 
     // unlock 
     return factor; 
    } 

public: 
    double getFactor(int key) { 
     // lock 
     map<int, double>::iterator it = cache.find(key); 
     // unlock 
     return (cache.end() == it) ? longRunningFetch(key) : it->second; 
    } 
}; 

FactorMaker & getFactorMaker() 
{ 
    static FactorMaker instance; 
    return instance; 
} 

class UsesFactors { 
public: 
    UsesFactors() {} 

    void printFactor(int key) const 
    { 
     cout << getFactorMaker().getFactor(key) << endl; 
    } 
}; 

int main(int argc, char *argv[]) 
{ 
    const UsesFactors obj; 

    for (int i = 0; i < 10; ++i) 
     obj.printFactor(i); 

    for (int i = 0; i < 10; ++i) 
     obj.printFactor(i); 

    return EXIT_SUCCESS; 
} 

(1)シングルトンパターンが大量に欠落する可能性があります。初めて見た方には夢中にならないようにしてください。

3

私はではありません。このキャッシュをローカルの静的にします。変更可能なマップは、の結果をキャッシングするソリューションです。それ以外の場合は、ローカル静的キャッシュがすべてのオブジェクトで同じであるため、クラスの異なるオブジェクトが同じキャッシュを共有するため、関数が役に立たなくなります。結果がオブジェクトに依存していない場合は、ローカルのstaticを使用できます。しかし、私はそれがどのような状態にもアクセスする必要がない場合、関数がなぜあなたのオブジェクトの非静的メンバーであるのか、自分自身に尋ねるでしょう。

あなたはスレッドセーフでなければならないと言います - 別のスレッドが同じオブジェクトのメンバ関数を呼び出すことができる場合は、おそらくミューテックスを使用します。boost::threadは良いライブラリです。

+1

これは、以前はオブジェクトの状態(変更可能なマップ)に依存していたので、関数はオブジェクトの非staticメンバです。ローカルの静的な場合、私はこの関数を静的にすることができます。 –

+0

これは別のものです。:) LongRunningOperationがあなたのオブジェクトに依存するメンバ関数であれば、これについてあなたに警告したいと思っています:)十分に注意することはできません:) –

関連する問題