Visual C++优化对大型数据集合的并发访问
2010-08-22 20:47:32 来源:WEB开发网与前面的构造块类不同,要向您解释清楚 Thread 类有一点儿困难(参见图 4)。对于那些了解 Java 的读者而言,它非常像 Java Thread 类,不同之处在于它不支持单独的 Run 接口 — 它要求直接从 Thread 派生。
对于那些不熟悉 Java 的线程机制的读者而言,以下是您使用 Thread 类的方法。首先,从 Thread 派生一个类(假设为 MyThread),然后重写纯虚拟方法 run。在该方法中放入您希望新线程执行的代码。然后,构建一个 MyThread 类型的对象,并调用 start。此时,新的线程将开始在您的 run 方法中执行。如果您希望另一个线程等待至 MyThread 线程退出,请调用 wait。(当然,您绝不能从 run 方法内部调用 wait 方法。)
以这种方式对线程进行建模的优点之一是:它使得与线程的通讯变得容易多了。要将信息传递给线程,您需要做的全部事情就是将这些信息存储在您的 MyThread 类的数据成员中。我通常在构造函数中完成这一工作。要向您的线程外部传递信息,只须完成相反的工作 — 在您的 run 方法中,将信息存储在您的 MyThread 类的数据成员中,然后实现 getter 方法以使其他线程可以获取该数据。
很重要的一点是您的线程类的任何数据成员都是私有的,并带有用于访问它们的 getter 和/或 setter 方法。这是因为将很可能从多个线程中访问这些数据成员,因此您需要使用封装所提供的控制来保证正确的线程同步。
Figure 5 Thread Implementation
Thread::Thread() : m_id(0), m_h(0)
{
}
Thread::~Thread()
{
if (m_h != 0)
{
wait();
CloseHandle(m_h);
m_id = 0;
m_h = 0;
}
}
void Thread::start()
{
if (m_h != 0)
{
wait();
CloseHandle(m_h);
m_id = 0;
m_h = 0;
}
unsigned id;
unsigned long h = _beginthreadex(0, 0, entryPoint, this,
CREATE_SUSPENDED, &id);
if (h == 0)
{
throw Exception(Exception::k_threadCreationFailure);
}
m_id = id;
m_h = reinterpret_cast<HANDLE>(h);
if (ResumeThread(m_h) == static_cast<DWORD>(-1))
{
throw Exception(Exception::k_threadResumeFailure, GetLastError());
}
}
unsigned __stdcall Thread::entryPoint(void* pArg)
{
try
{
Thread* pThis = static_cast<Thread*>(pArg);
pThis->run();
}
catch (const Exception& e)
{
cout << "Thread " << e.getErrorMsg() << endl << endl;
}
catch (const exception& e)
{
cout << "std::exception in thread: " << e.what() << endl << endl;
}
return 0;
}
bool Thread::wait(unsigned long timeout)
{
bool result = true;
if (m_h != 0)
{
switch (WaitForSingleObject(m_h, timeout))
{
case WAIT_OBJECT_0:
break;
case WAIT_TIMEOUT:
result = false;
break;
default:
throw Exception(Exception::k_threadWaitFailure,
GetLastError());
break;
}
}
return result;
}
图 5
Thread 类的实现有几个难点(参见图 5)。首先,请观察私有和静态 entryPoint 方法:
static unsigned __stdcall entryPoint(void* pArg);
该方法是传递给 _beginthreadex API 以启动线程运行的函数。您不能传递一个非静态方法(如 run),因为非静态方法的调用约定与 _beginthreadex 预期的调用约定不同。(它不知道如何传递该指针。)但是,被声明为 __stdcall 的静态方法将完全适用。对 _beginthreadex API(它位于 start 方法中)的调用会传递一个指向 entryPoint 方法的指针和 this 指针。当新的线程开始在 entryPoint 中执行时,pArg 参数将是传递给 _beginthreadex 的 this 指针。然后,entryPoint 方法将它转换为 Thread* 并调用 run。虚拟方法的魔力将接管工作,并将执行传递给您的 MyThread::run 方法。
entryPoint 方法还具有另外一个用途。它为最可能的异常类型提供最后的异常处理程序。这可以确保您在某个环节恰好出错时,能够获得有用的错误消息。
Thread 类的另外一个微妙的方面值得提一下。在 start 方法中,您将注意到线程是在挂起状态中创建的,然后线程 ID 和句柄被赋给类成员。最后,线程被恢复执行。这一挂起是必要的,可防止出现竞争情形。假设新线程是在运行状态中创建的。如果新线程在 run 方法中比较早地调用了 getId 或 getHandle,则它可能在 start 方法已经进行赋值之前访问这些成员。
- ››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++应用程序
更多精彩
赞助商链接