Frage

Ich habe eine relativ teure Datenabrufoperation, die ich die Ergebnisse gecached werden soll. Dieser Vorgang wird von const Methoden aufgerufen, in etwa wie folgt aus:

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

Ich würde gerne AdjustData const bleiben, aber ich möchte den Faktor zwischenzuspeichern, so ich es holen nur das erste Mal. Derzeit verwende ich ein mutable map<int, double> das Ergebnis zu speichern (die Karte von key sein factor), aber ich denke, eine Funktion-scoped statisch unter Verwendung könnte eine bessere Lösung sein - dieser Faktor nur durch diese Funktion benötigt wird, und ist irrelevant für den Rest der Klasse.

Ist das scheint wie ein guter Weg zu gehen? Gibt es bessere Möglichkeiten? Welche Dinge könnte ich darüber nachdenken, vor allem im Hinblick auf die Thread-Sicherheit.

Danke,

Dom

War es hilfreich?

Lösung

Ich würde die Umsetzung von LongRunningOperationToFetchFactor mit so etwas wie diese wickeln. Ich bin mit Boost-scoped Schlössern aber man kann so etwas ähnliches mit anderen Verriegelungsrahmen.

#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;
}

Wenn dies wirklich ein langer laufender Betrieb dann die Kosten für die Mutex der Verriegelung sollten minimal sein.

Es war nicht ganz klar, von Ihnen in Frage, aber wenn die Funktion LongRunningOperationToFetchFactor eine Member-Funktion von Ihnen Klasse ist dann wollen Sie die Karte, die wandelbar Karte sein in derselben Klasse. Ich einzelne statische Mutex für den Zugang ist aber immer noch schnell genug.

Andere Tipps

Ich würde nicht machen diesen Cache eine lokale statisch. Die wandelbare Karte ist die Lösung für die Caching Ergebnisse. Sonst wird es Ihre Funktion nutzlos machen, wie verschiedene Objekte der Klasse den gleichen Cache teilen, wie die lokale statische Cache der für alle Objekte gleich ist. Sie können die lokale statische verwenden, wenn das Ergebnis nicht auf das Objekt allerdings abhängig ist. Aber dann würde ich mich fragen, warum die Funktion ein nicht-statisches Element des Objekts ist, wenn es keinen Staat darauf zugreifen muss.

Wie Sie sagen, es sollte Thread-sicher sein - wenn verschiedene Threads die Member-Funktion auf dem gleichen Objekt aufrufen können, möchten Sie wahrscheinlich einen Mutex verwenden. boost::thread ist eine gute Bibliothek zu verwenden.

Sie können die Singletonmuster (1) zu einer Klasse, die die lange führt -running Betrieb und speichert das Ergebnis. Diese Instanz könnte dann in const Member-Funktionen von anderen Klassen verwendet werden. Betrachten mutual exclusion Einsätze und Extraktionen aus der Kartendatenstruktur für Thread-Sicherheit zu schützen. Wenn Multi-Threaded-Performance ein riesiges Problem ist, dann können Sie Flagge Tasten wie im Gang, um mehrere Threads zu verhindern gleichzeitig den gleichen Schlüssel berechnet wird.

#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) Die Singletonmuster grob übersehen werden. So verzichten, bitte aus mit ihm verrückt, wenn man es zum ersten Mal sehen.

Es sei denn, ich verstehe nicht, scheint es mir offensichtlich, dass Sie dies eine statische machen wollen:

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

So können Sie nur einmal den Faktor holen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top