Visual C++优化对大型数据集合的并发访问
2010-08-22 20:47:32 来源:WEB开发网当 CD 启动时,它将首先解释其命令行参数,然后创建数据库(参见图 7)。接下来,CD 将打印输出标题。(如果它在您看来不像是合法的 C++,则您尚未学习有关便利的 C 预处理器功能的内容。该预处理器将所有仅由空格分隔的字符串文字连接在一起。)最后,CD 针对命令行上指定的每个线程数目调用 doTest 两次。第一次测试以最明显的方式同步对数据库的访问。第二次使用我的经过优化的技巧。为了确保得到有用的错误消息,主函数还会捕获最可能的异常类型。
Figure 8 doTest
typedef vector<auto_ptr<WorkerThread> > ThreadList;
typedef ThreadList::const_iterator ThreadIter;
static void doTest(Database& db, unsigned long numIterations,
unsigned long numThreads)
{
// Set up thread pool:
ThreadList threads;
threads.reserve(numThreads);
for (unsigned i = 0; i < numThreads; i++)
{
WorkerThread* pThread = new WorkerThread(i, numThreads,
numIterations, db);
threads.push_back(auto_ptr<WorkerThread>(pThread));
}
Timer timer;
// Start threads running:
for (ThreadIter j = threads.begin(); j != threads.end(); j++)
{
(*j)->start();
}
// Wait on threads:
for (ThreadIter k = threads.begin(); k != threads.end(); k++)
{
(*k)->wait();
}
timer.stop();
cout << ',' << timer.getMilliSec() << flush;
}
图 8
图 8 显示了 doTest 函数。该函数开始时创建了一个线程池,它是指向 WorkerThread 对象的指针的向量。实际上,更准确地说,它是 auto_ptr 对象的向量,以便在销毁该数组时,还将删除所有 WorkerThread 对象。在下一部分中,我将详细讨论 WorkerThread 类。
接下来,doTest 创建一个计时器来对测试进行计时,启动每个线程运行,并且等待各个线程以确保它们全部完成该测试。最后,DoTest 停止计时器并打印结果。请注意,输出格式有一点儿奇怪。它采用逗号分隔值 (CSV) 格式。这样,我可以将输出重定向到具有 .csv 文件扩展名的文件中,并将其直接导入 Microsoft Excel。这可以快速而轻松地创建漂亮的图表。
Figure 9 WorkerThread Class Declaration
class WorkerThread : public Thread
{
public:
WorkerThread(unsigned long threadNum, unsigned long numThreads,
unsigned long numIters, Database& db);
virtual ~WorkerThread();
protected:
virtual void run();
private:
enum RequestType { k_read, k_update };
void getRequest(unsigned long& entryId, RequestType& requestType,
string& newValue);
void doUpdate(unsigned long entryId, const string& newValue);
string doRead(unsigned long entryId);
static void simulateWork(unsigned long iterations);
enum
{
k_randMax = 1771875ul,
k_randCoeff = 2416ul,
k_randOffset = 374441ul
};
unsigned long rand(unsigned long hi);
unsigned long m_randSeed;
unsigned long m_threadNum;
unsigned long m_numThreads;
unsigned long m_numIters;
Database& m_db;
};
Figure 10 WorkerThread Class Implementation
WorkerThread::WorkerThread(unsigned long threadNum,
unsigned long numThreads, unsigned long numIters, Database& db) :
m_randSeed(threadNum % k_randMax),
m_threadNum(threadNum),
m_numThreads(numThreads),
m_numIters(numIters),
m_db(db)
{
}
WorkerThread::~WorkerThread()
{
}
void WorkerThread::run()
{
// The strange starting point and step size in this loop make sure
// that there are the correct total number of iterations among all
// the threads, even if m_numIters is not evenly divisible by
// m_numThreads.
for (unsigned long i = m_threadNum; i < m_numIters;
i += m_numThreads)
{
// Get a request:
unsigned long entryId;
RequestType requestType;
string value;
getRequest(entryId, requestType, value);
// Process the request:
if (requestType == k_update)
{
doUpdate(entryId, value);
}
else
{
value = doRead(entryId);
}
// In a real-world server, the result would be marshaled back
// to the client here.
}
}
void WorkerThread::getRequest(unsigned long& entryId,
RequestType& requestType, string& newValue)
{
const unsigned long k_numReadsPerUpdate = 5;
entryId = rand(m_db.getNumEntries() - 1);
requestType = (rand(k_numReadsPerUpdate) == 0)
? k_update
: k_read;
if (requestType == k_update)
{
newValue = "new value";
}
}
void WorkerThread::doUpdate(unsigned long entryId, const string& newValue)
{
// Update the database:
DBEntry& entry = m_db.getEntry(entryId);
LockKeeper<DBEntry> keeper(entry);
entry.setValue(newValue);
// Simulate update overhead:
simulateWork(100);
// Unlock entry as we return from this method
}
string WorkerThread::doRead(unsigned long entryId)
{
// Read from the database:
DBEntry& entry = m_db.getEntry(entryId);
LockKeeper<DBEntry> keeper(entry);
string readResult = entry.getValue();
// Simulate read overhead:
simulateWork(10);
return readResult;
// Unlock entry as we pass out of this scope
}
static double g_dummy = 0; // prevents optimizing simulateWork away
void WorkerThread::simulateWork(unsigned long iterations)
{
for (unsigned long i = 0; i < iterations; i++)
{
// Meaningless work to take up time:
unsigned long x = i % 37;
g_dummy = cos(x) + sin(x) + cosh(x) + sinh(x);
}
}
// This is a very simple pseudo-random number generator, which overcomes
// two limitations of the built-in rand() function:
// * Its maximum is considerably higher, and
// * It doesn't serialize calling threads.
// Derived from "Numerical Recipes in C", by Press, Flannery, Teukolsky,
// and Vetterling, ©1988 by Cambridge University Press, pp.209-211.
unsigned long WorkerThread::rand(unsigned long hi)
{
assert(hi < k_randMax);
m_randSeed = (m_randSeed * k_randCoeff + k_randOffset) % k_randMax;
return (hi * m_randSeed) / (k_randMax - 1);
}
- ››Visual Basic 2008 数学函数
- ››Visual Studio2005中Smart Device的问题
- ››Visual Studio 中根据数据库字段动态生成控件
- ››Visual Studio 11全新黑色主题
- ››Visual Studio 2011 Beta新特性(一):安装VS201...
- ››Visual Studio自定义调试窗体两个小技巧
- ››Visual Studio 2005 Team Edition for Database P...
- ››Visual C#两分钟搭建BHO IE钩子
- ››Visual C++优化对大型数据集合的并发访问
- ››优化精髓之商业性网站常遇见的问题和误区
- ››大型网站的域名分布策略
- ››优化增强您的Visual C++应用程序
更多精彩
赞助商链接