1. 程式人生 > >從Win32 API封裝Thread類[2]

從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 functor19 template <class T>20 struct FuncImpl {
21 static Runnable* transfer(const T&
t) {
22 returnnew 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 function35 template <class T>36 struct FuncImpl<T (*)()> {
37 static Runnable* transfer(T (*
t)()) {
38 returnnew 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 function13 void print1(int n) {
14     cout <<"print1"<<
 endl;
15 
}
16 
17 //binary function18 void print2(int m, int n) {
19     cout <<"print2"<<
 endl;
20 
}
21 
22 23 //no argument functor24 struct PrintFunctor {
25 void operator()() const
 {
26         cout <<"PrintFunctor"<<
 endl;
27 
    }
28 
};
29 
30 //unary functor31 struct PrintFunctor1 : public unary_function<intvoid> {
32 void operator()(int n) const
 {
33         cout <<"PrintFunctor1"<<
 endl;
34 
    }
35 
};
36 
37 //binary functor38 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 function47     Thread thread1(&print);
48 
    thread1.start();
49 
50 //construct Thread with unary function51     Thread thread2(bind(ptr_fun(print1), 5));
52 
    thread2.start();
53 
54 //construct Thread with binary function55     Thread thread3(bind(bind1st(ptr_fun(print2), 1), 2));
56 
    thread3.start();
57 
58 59 //construct Thread with no argument functor60     Thread thread4((PrintFunctor()));
61 
    thread4.start();
62 
63 //construct Thread with unary functor64     Thread thread5(bind(PrintFunctor1(), 5));
65 
    thread5.start();
66 
67 //construct Thread with binary functor68     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 return0;
79 }

當然了,上面的並不是全部,修改後的Thread類不僅能夠使用原先的從Thread類繼承或者實現Runnable介面的方法,還可以使用任何無參函式或無參函式物件。除了test.cpp裡示範的,你甚至可以用bind,bind1st,bind2st,mem_fun,mem_fun_ref的組合來用某個類的成員函式作為引數,具有超強的靈活性。

目前實現的這些都是Thread類最基本的功能,其他功能如設定執行緒優先順序,掛起或恢復執行緒,異常處理等具體實現都比較簡單,這這裡就不一一實現了。
原始碼下載:點選下載

posted on 2007-08-31 11:58 螞蟻終結者 閱讀(2968) 評論(7)  編輯 收藏 引用 所屬分類: C++