#ant

The dreams in which I'm dying are the best I've ever had...

从Win32 API封装Thread类[2]

在上一篇中介绍了创建Thread的两种方法:从Thread类继承或者实现Runnable接口。有时候这并不是特别方便,我们需要的是更灵活的方法,比如像boost库中的Thread一样可以用普通函数和函数对象
(functor and function object)作为构造函数参数。如果你熟悉STL,你应该熟悉bind1st和bind2nd这两个函数配接器(function adapter),bind1st和bind2nd可以将一个二元函数(binary function)转换成一元函数(unary function)。为了使Thread类能够用普通函数和函数对象作为参数,我们需要一个bind将一元函数转换成无参函数:
bind.h
 1 #ifndef BIND_H
 2 
#define BIND_H
 3 

 4 template <class _Result>
 5 struct trivial_function {
 6 
    typedef _Result result_type;
 7 
};
 8 

 9 template <class _Operation>
10 class binder : public trivial_function<typename _Operation::result_type> {
11 public
:
12     binder(const _Operation& x, const typename _Operation::argument_type&
y)
13 
        :op(x), value(y) {}
14     typename _Operation::result_type operator()() const
 {
15         return
 op(value);
16 
    }
17 protected
:
18 
    _Operation op;
19 
    typename _Operation::argument_type value;
20 
};
21 

22 template <class _Operation, class _Tp>
23 inline binder<_Operation> 
24 bind(const _Operation& __fn, const _Tp&
 __x) 
25 
{
26 
    typedef typename _Operation::argument_type _Arg_type;
27     return binder<_Operation>
(__fn, _Arg_type(__x));
28 
}
29 

30 #endif/*BIND_H*/

有了bind我们还需要修改Thread类的构造函数,显然我们必须将构造函数声明为成员模板(还有一种方法也可以达到同样的目的,就是把Thread类声明为模板,但是这样的设计好像不太好),这样才能够让Thread类的构造函数可以接受各种类型的参数,修改后的构造函数应该能够使用如下三种类型的参数:
1.Runnable *
2.no argument function
3.no argument functor
下面是修改后的头文件:
runnable.h
 1 #ifndef RUNNABLE_H
 2 
#define RUNNABLE_H
 3 

 4 struct Runnable {
 5     virtual void run() = 0
;
 6     virtual ~
Runnable() {}
 7 
};
 8 

 9 template <class T>
10 class RunnableFunctor : public Runnable {
11 public
:
12     RunnableFunctor(const T&
f) :_func(f) {}
13     virtual void
 run() { _func(); }
14 private
:
15 
    T _func;
16 
};
17 

18 //base template for no argument functor
19 template <class T>
20 struct FuncImpl {
21     static Runnable* transfer(const T&
t) {
22         return new RunnableFunctor<T>
(t);
23 
    }
24 
};
25 

26 //partial specialization for T*
27 template <class T>
28 struct FuncImpl<T*> {
29     static Runnable* transfer(T*
t) {
30         return
 t;
31 
    }
32 
};
33 

34 //partial specialization for no argument function
35 template <class T>
36 struct FuncImpl<T (*)()> {
37     static Runnable* transfer(T (*
t)()) {
38         return new RunnableFunctor<T (*)()>
(t);
39 
    }
40 
};
41 

42 template <class T>
43 inline Runnable* transfer(const T& t) {
44     return FuncImpl<T>
::transfer(t);
45 
}
46 

47 #endif/*RUNNABLE_H*/

thread.h
 1 #ifndef THREAD_H
 2 
#define THREAD_H
 3 

 4 #include <windows.h>
 5 #include "bind.h"
 6 #include "runnable.h"
 7 
 8 #define CLASS_UNCOPYABLE(classname) \
 9     private
: \
10     classname(const classname&
); \
11     classname& operator=(const classname&
);
12 

13 class Thread : public Runnable {
14 
    CLASS_UNCOPYABLE(Thread)
15 public
:
16 
    Thread()
17         :_target(0
)
18         ,_handle(0
) {
19 

20     }
21     template <class T>

22     explicit Thread(const T& op)
23 
        :_target(transfer(op))
24         ,_handle(0
) {
25 

26     }
27     virtual ~
Thread();
28     virtual void
 run() {}
29     void
 start();
30     void
 join();
31 private
:
32     static unsigned __stdcall threadProc(void*
param);
33 private
:
34     Runnable*
_target;
35 
    HANDLE _handle;
36 
};
37 

38 #endif/*THREAD_H*/

thread.cpp和前一篇的几乎一样,唯一的不同是去掉了构造函数Thread(Runnable *),因为现在的构造函数改成了成员模板,实现也放在thread.h中了。现在的构造函数能够接受各种类型的参数,主要归功于模板函数transfer,实现代码在runnable.h中,主要技巧是用类的偏特化模拟函数模板的偏特化,不明白的请看为什么不要特化函数模版

下面是测试代码:
test.cpp
 1 #include "thread.h"
 2 #include <iostream>
 3 #include <functional>
 4 
 5 using namespace std;
 6 

 7 //no argument function
 8 void print() {
 9     cout << "print" <<
 endl;
10 
}
11 

12 //unary function
13 void print1(int n) {
14     cout << "print1" <<
 endl;
15 
}
16 

17 //binary function
18 void print2(int m, int n) {
19     cout << "print2" <<
 endl;
20 
}
21 

22 
23 //no argument functor
24 struct PrintFunctor {
25     void operator()() const
 {
26         cout << "PrintFunctor" <<
 endl;
27 
    }
28 
};
29 

30 //unary functor
31 struct PrintFunctor1 : public unary_function<intvoid> {
32     void operator()(int n) const
 {
33         cout << "PrintFunctor1" <<
 endl;
34 
    }
35 
};
36 

37 //binary functor
38 struct PrintFunctor2 : public binary_function<intintvoid> {
39     void operator()(int m, int n) const
 {
40         cout << "PrintFunctor2" <<
 endl;
41 
    }
42 
};
43 

44 int main() {
45 

46     //construct Thread with no argument function
47     Thread thread1(&print);
48 
    thread1.start();
49 

50     //construct Thread with unary function
51     Thread thread2(bind(ptr_fun(print1), 5));
52 
    thread2.start();
53 

54     //construct Thread with binary function
55     Thread thread3(bind(bind1st(ptr_fun(print2), 1), 2));
56 
    thread3.start();
57 

58 
59     //construct Thread with no argument functor
60     Thread thread4((PrintFunctor()));
61 
    thread4.start();
62 

63     //construct Thread with unary functor
64     Thread thread5(bind(PrintFunctor1(), 5));
65 
    thread5.start();
66 

67     //construct Thread with binary functor
68     Thread thread6(bind(bind1st(PrintFunctor2(), 1), 2));
69 
    thread6.start();
70 

71     thread1.join();
72 
    thread2.join();
73 
    thread3.join();
74 
    thread4.join();
75 
    thread5.join();
76 
    thread6.join();
77 

78     return 0;
79 }

当然了,上面的并不是全部,修改后的Thread类不仅能够使用原先的从Thread类继承或者实现Runnable接口的方法,还可以使用任何无参函数或无参函数对象。除了test.cpp里示范的,你甚至可以用bind,bind1st,bind2st,mem_fun,mem_fun_ref的组合来用某个类的成员函数作为参数,具有超强的灵活性。

目前实现的这些都是Thread类最基本的功能,其他功能如设置线程优先级,挂起或恢复线程,异常处理等具体实现都比较简单,这这里就不一一实现了。
源代码下载:点击下载

posted on 2007-08-31 11:58 蚂蚁终结者 阅读(3225) 评论(7)  编辑 收藏 引用 所属分类: C++

Feedback

# re: 从Win32 API封装Thread类[2] 2007-08-31 12:26 haskell

不错,支持
介绍一个完美的c++多线程库,intel的tbb。
http://osstbb.intel.com/index.php  回复  更多评论   

# re: 从Win32 API封装Thread类[2] 2007-08-31 15:14 重剑

我download了源码,看了下,有 new 没 delete, why?  回复  更多评论   

# re: 从Win32 API封装Thread类[2] 2007-08-31 15:26 蚂蚁终结者

@重剑
oops!!!
delete不小心忘记写了。
把析构函数改成这样就行了:
Thread::~Thread() {
if (_handle != 0)
CloseHandle(_handle);
if (_target != 0)
delete _target;
}

已经更新了下载链接,也可以重新下载。
thanks!  回复  更多评论   

# re: 从Win32 API封装Thread类[2] 2007-08-31 21:03 C/C++面试题

支持  回复  更多评论   

# re: 从Win32 API封装Thread类[2] 2007-09-03 12:37 shaker(太子)

如果能使用boost::bind 代码就漂亮多了  回复  更多评论   

# re: 从Win32 API封装Thread类[2] 2007-09-03 21:10 蚂蚁终结者

@shaker(太子)
boost::bind确实优雅
上面的bind修改一下应该也可以实现差不多的功能  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理