WEB开发网
开发学院软件开发VC Visual C++优化对大型数据集合的并发访问 阅读

Visual C++优化对大型数据集合的并发访问

 2010-08-22 20:47:32 来源:WEB开发网   
核心提示:与前面的构造块类不同,要向您解释清楚 Thread 类有一点儿困难(参见图 4),Visual C++优化对大型数据集合的并发访问(4),对于那些了解 Java 的读者而言,它非常像 Java Thread 类,假设新线程是在运行状态中创建的,如果新线程在 run 方法中比较早地调用了 getId 或 getHandl

与前面的构造块类不同,要向您解释清楚 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 方法已经进行赋值之前访问这些成员。

上一页  1 2 3 4 5 6 7 8 9  下一页

Tags:Visual 优化 大型

编辑录入:爽爽 [复制链接] [打 印]
赞助商链接