简单模拟boost::thread

2009年10月4日 | 分类: C/C++ | 标签: , ,

本来只想学习asio的,却被boost的强大所吸引,几乎两天都在看boost文档,还好html的比较好看,比起看Intel手册是舒服多了。

boost的thread类可以用任意函数来做线程函数,甚至可以是类的非静态成员函数,并且函数的返回类型和参数都不限(参数个数限制为最多9个)。MFC程序员渴望的用类的非静态成员函数做线程函数用boost::thread得以几乎完美的实现。

下面是个十分简化的模拟boost::thread,主要实现可以用任意函数做线程函数,包括类的非静态成员函数。如下:

#include <iostream>
#include <functional>    // std::tr1::bind
#include <windows.h>
 
 
class MyThread
{
private:
  class ThreadDataBase
  {
  public:
    ThreadDataBase()
    {}
 
    virtual ~ThreadDataBase()
    {}
 
    virtual void Run() = 0;
  };
 
  // 用于保存线程相关的信息结构,这里只保存了MyThread构造函数
  // 时获得的F对象,一般是由bind返回的functor。
  // boost::thread类里的这个结构还保存了线程句柄等其它信息
  template <typename F>
  class ThreadData 
    : public ThreadDataBase
  {
  private:
    F f;
  public:
    ThreadData(F _f) : f(_f)
    {}
 
    void Run()
    {
      f();  // 调用functor
    }
  };
 
  // 线程句柄
  HANDLE m_hThread;
 
  // 因为不知道typename,所以不能定义ThreadData指针,所以建了个
  // 空基类,然后利用虚函数的特性调用派生类的Run()
  ThreadDataBase* pThreadData;
 
  // 线程中介函数,内部调用functor
  static DWORD CALLBACK ThreadProxy(LPVOID lpParam)
  {
    ThreadDataBase *pData = reinterpret_cast<ThreadDataBase*>(lpParam);
    pData->Run();
    delete pData;
    return 0;
  }
 
public:
  template <class F>
  explicit MyThread(F f)
    : pThreadData(new ThreadData<F>(f))    // 保存好functor
  {
    m_hThread = CreateThread(NULL, 0, &ThreadProxy, pThreadData, 0, NULL);
  }
 
  // 类似boost::thread::join()
  void Wait()
  {
    WaitForSingleObject(m_hThread, INFINITE);
  }
};
 
// 测试类
class Test
{
public:
  void TestThread(int nArg1, int nArg2)
  {
    std::cout << "Thread in class, Arg1 = " << nArg1 << ", Arg2 = " << nArg2 << std::endl;
  }
};
 
int main(int argc, char* argv[])
{
  Test test;
 
  // 使用类的非静态成员函数做线程函数
  // 而且类成员函数的返回类型和参数都没有限制
  MyThread thrd(std::tr1::bind(&Test::TestThread, &test, 123, 321));
  thrd.Wait();
 
  return 0;
}

这里用了std::tr1库,没用boost,请使用VC++ 2008 SP1编译(据说VC++ 2010已经把std::tr1下的东东移到了std下)。

实现用任意函数做线程函数主要依赖于std::tr1::bind(类似于boost::bind),std::tr1::bind会生成一个可调用的函数对象,

std::tr1::bind(&Test::TestThread, &test, 123, 321)

其实就基本上等于下面的伪代码:

class functor
{
public:
  void operator()
  {
    (&test)->TestThread(123, 321);
  }
};

所以MyThread::ThreadData::Run里调用的f()实际上就等于调用了functor(),也就间接的调用了(&test)->TestThread(123, 321)。

MyThread里有个静态成员函数ThreadProxy,ThreadProxy内部去调用了构造MyThread时获得的函数,然后返回0,boost::thread里也有这样一个函数,叫thread_start_function。
可以看到用MyThread创建的线程永远返回0(GetExitCodeThread),boost::thread也是这样。为什么这样?可以思考这样一个问题,在Windows上,线程都要求要有一个返回值,但如果传递给boost::thread一个返回void的函数,那线程该返回什么呢?所以通通让它返回0算了。

刚接触boost,上面内容如有错误请高手斧正。

  1. 2009年10月10日14:56

    真正的程序员用C++,我以前自学过这个,呵呵!

  2. 2009年12月16日21:21

    boost在平时工作中也用的,不过有些小项目,如果把boost编译链接进去,太大了……
    你这个代码,用插件显示的么?是什么插件啊?

    • 2009年12月17日01:28

      代码高亮用的是WP-Syntax插件,自己改了点颜色配置。