從Win32 API封裝Thread類[2]
阿新 • • 發佈:2018-12-27
在上一篇中介紹了建立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<int, void> {
32 void operator()(int n) const {
33 cout <<"PrintFunctor1"<< endl;
34 }
35 };
36 37 //binary functor38 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 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類最基本的功能,其他功能如設定執行緒優先順序,掛起或恢復執行緒,異常處理等具體實現都比較簡單,這這裡就不一一實現了。
原始碼下載:點選下載
(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 {
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()()
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;
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<int, void> {
32 void operator()(int n) const {
33 cout <<"PrintFunctor1"<< endl;
34 }
35 };
36 37 //binary functor38 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 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++