在上一篇中介绍了创建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<int, void> {
32 void operator()(int n) const {
33 cout << "PrintFunctor1" << endl;
34 }
35 };
36
37 //binary functor
38 struct PrintFunctor2 : public binary_function<int, int, void> {
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类最基本的功能,其他功能如设置线程优先级,挂起或恢复线程,异常处理等具体实现都比较简单,这这里就不一一实现了。
源代码下载:
点击下载