10

我有一个相对昂贵的数据获取操作,我想缓存它的结果。这个操作是从const方法中调用的,大致是这样的:

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

我想AdjustData留下来const,但我想缓存这个因素,所以我只在第一次获取它。目前我正在使用 amutable map<int, double>来存储结果(地图是 from keyto factor),但我认为使用函数范围的静态可能是一个更好的解决方案 - 这个因素只需要这个函数,并且与其余的班级。

这似乎是一个好方法吗?有没有更好的选择?我可能会想到什么,特别是在线程安全方面。

谢谢,

多姆

4

4 回答 4

6

我会用这样的东西来包装 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;
}

如果这确实是一个长时间运行的操作,那么锁定 Mutex 的成本应该是最小的。

你的问题不是很清楚,但如果函数 LongRunningOperationToFetchFactor 是你类的成员函数,那么你希望映射是同一个类中的可变映射。不过,我使用单个静态互斥锁进行访问仍然足够快。

于 2009-03-05T14:56:17.300 回答
5

不会让这个缓存成为本地静态的。可变映射是缓存结果的解决方案。否则它将使您的函数无用,因为您的类的不同对象将共享相同的缓存,因为本地静态缓存对于所有对象都是相同的。如果结果不依赖于对象,您可以使用本地静态。但是然后我会问自己,为什么该函数是对象的非静态成员,如果它不需要访问它的任何状态。

正如你所说,它应该是线程安全的——如果不同的线程可以在同一个对象上调用成员函数,你可能想要使用互斥锁。boost::thread是一个很好用的库。

于 2009-03-05T15:24:00.587 回答
1

您可以将单例模式(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) 单例模式可能会被严重遗漏。因此,如果您是第一次看到它,请不要为之疯狂。

于 2009-03-05T14:49:22.930 回答
0

除非我不明白,否则对我来说很明显你想让它成为一个静态的:

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

这样,您只需获取一次因子。

于 2009-03-05T14:09:34.243 回答