#ant

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

从Win32 API封装Thread类[1]

前几天在学Windows多线程程序设计,发现Win32 API用起来确实不怎么方便,特别是对于C++程序员。于是实现了一个简单的封装,技术含量当然不高,不过用起来还是比较方便的。如果你熟悉Java,你会发现这个实现有点像Java的Thread,在Java中有两种方法可以创建一个Thread:

1.从Thread类继承并实现run方法:

1 class MyThread extends  Thread {
2     public void
 run() {
3 
        ...
4 
    } 
5 
};
6 

7 //开启线程
8 MyThread thread = new  MyThread;
9 thread.start();

2.声明一个类实现Runnable接口并实现run方法:

1 class MyRunnable implements  Runnable {
2     public void
 run() {
3 
        ...
4 
    }
5 
};
6 

7 // 开启线程 
8 MyThread thread = new MyThread(new  MyRunnable);
9 thread.start();


具体实现
Java的实现方式还算优雅,我们也可以在C++中模拟,由于篇幅所限,在这里我们只关注基本功能的实现:
thread.h
 1 #ifndef THREAD_H
 2 
#define THREAD_H
 3 

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

11 struct Runnable {
12     virtual void run() = 0
;
13     virtual ~
Runnable() {}
14 
};
15 

16 class Thread : public Runnable {
17 
    CLASS_UNCOPYABLE(Thread)
18 public
:
19     explicit Thread(Runnable* target = 0
);
20     virtual ~
Thread();
21     virtual void
 run() {}
22     void
 start();
23     void
 join();
24 private
:
25     static unsigned __stdcall threadProc(void*
param);
26 private
:
27     Runnable*
_target;
28 
    HANDLE _handle;
29 
};
30 

31 #endif/*THREAD_H*/

在上面的代码中我们先定义一个Runnable类,并为Runnable类添加纯虚函数run,需要特别注意的是Runnable类的虚析构函数,任何想要成为基类的class都应该声明析构函数为virtual。
Thread类直接继承自Runnable,CLASS_UNCOPYABLE(Thread) 这一行用宏声明Thread类不可拷贝,具体细节请看 考虑用Macro替换Uncopyable 。start成员函数和Java中的一样,用来开启一个线程,join成员函数用来阻塞当前线程直到开启的线程执行完毕。threadProc作为静态成员函数用来传递给_beginthreadex,至于为什么用_beginthreadex代替CreateThread请问Google。初步的实现都比较简单,直接贴上代码:
thread.cpp
 1 #include "thread.h"
 2 #include <process.h> // for _beginthreadex
 3 
 4 Thread::Thread(Runnable* target /*= 0*/)
 5 :_target(target
)
 6 ,_handle(0
) {
 7 

 8 }
 9 

10 Thread::~Thread() {
11     if (_handle != 0
)
12 
        CloseHandle(_handle);
13     if (_target != 0
)
14 
        delete _target;
15 
}
16 

17 void Thread::start() {
18     if (_handle != 0
)
19         return
;
20 
    unsigned id;
21     _handle = reinterpret_cast<HANDLE>
(
22         _beginthreadex(00, threadProc, this0&
id)
23 
        );
24 
}
25 

26 void Thread::join() {
27     if(_handle != 0
) {
28 
        WaitForSingleObject(_handle, INFINITE);
29 
        CloseHandle(_handle);
30         _handle = 0
;
31 
    }
32 
}
33 

34 unsigned __stdcall Thread::threadProc(void* param) {
35     Thread*= static_cast<Thread*>
(param);
36     if (p->_target != 0
)
37         p->_target->
run();
38     else

39         p->run();
40     return 0
;
41 }

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

 6 //第一种方法,从Thread类继承
 7 struct MyThread : public Thread {
 8     virtual void
 run() {
 9         for (int i = 0; i < 5++
i) {
10             cout << "MyThread Running..." << i <<
 endl;
11             Sleep(100
);
12 
        }
13 
    }
14 
};
15 

16 //第二种方法,“实现”Runnable接口
17 struct MyRunnable : public Runnable {
18     virtual void
 run() {
19         for (int i = 0; i < 5++
i) {
20             cout << "MyRunnable Running..." << i <<
 endl;
21             Sleep(300
);
22 
        }
23 
    }
24 
};
25 

26 int main() {
27 

28     MyThread thread1;
29     Thread thread2(new
 MyRunnable());
30 
    thread1.start();
31 
    thread2.start();
32 
    thread1.join();
33 
    thread2.join();
34 

35     return 0;
36 }

可能的运行结果:
MyThread Running...0
MyRunnable Running...0
MyThread Running...1
MyThread Running...2
MyRunnable Running...1
MyThread Running...3
MyThread Running...4
MyRunnable Running...2
MyRunnable Running...3
MyRunnable Running...4


如果你熟悉boost库,你会知道boost库的Thread构造函数可以接受普通函数和函数对象作为参数,如果你觉得从Thread类继承或者实现Runnable接口还不够简洁,下一篇会有一个比较好的改进。

posted on 2007-08-30 10:18 蚂蚁终结者 阅读(4587) 评论(8)  编辑 收藏 引用 所属分类: C++

Feedback

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

受益匪浅啊   回复  更多评论   

# re: 从Win32 API封装Thread类[1] 2008-02-26 17:51 sd

太简单了  回复  更多评论   

# re: 从Win32 API封装Thread类[1] 2008-07-25 22:46 Robinfoxnan

看了楼主的TEA,和strlen觉得分析的很好,
但是,关于多线程,
在侯捷的《windows多线程程序设计》中说,建议不使用_beginthreadex之类纯API函数,原因是在使用了线程本地存储的函数,比如GetLastError()之类的函数后,运行库比如VC++,会帮你建立一些我们看不到的结构体,会容易导致内存泄露,建议使用AfxBeginThread()类,特定开发环境提供的函数或者类,比如CWinThread。
可以参考该书第8章!  回复  更多评论   

# re: 从Win32 API封装Thread类[1] 2008-10-10 01:23 OwnWaterloo

@Robinfoxnan
我帮楼主回你了 ~~

侯捷的书, 肯定说的是不要使用 CreateTread 这个API
如果他说的是不要使用 _beginthreadex , 那他该被拖出去打。

至于AfxBeginThread ,windows平台下写程序一定要和MFC扯上关系么?

既然你提到了AfxBeginThread, 你可以去看一下MFC的源码, MFC80, 它就是使用的 _beginthreadex ~~

  回复  更多评论   

# re: 从Win32 API封装Thread类[1] 2008-10-10 01:36 OwnWaterloo

我有2个建议, 一个可以慎重考虑一下, 另外一个可以完全不理睬。
还有2个疑问 ……

1.单下划线开始的标识符?

2.将windows.h 从thread.h中移走
.h
class Thread {
//
private:
uintptr_t handle_;
}

.cpp
handle_ = _beginthreadex 这个可以直接赋值
CloseHandle( reinterpret_cast<HANDLE>( handle_ ); 这个要自己转
虽然麻烦一点, 但是将 windows.h 隔离到了头文件之外




疑问:
1. Thread的析构

10 Thread::~Thread() {
11 if (_handle != 0)
12 CloseHandle(_handle);
13 if (_target != 0)
14 delete _target;
15 }

CloseHandle 线程并不停止
如果主线程没有join delete _target; 后 线程还在run

这是线程函数
34 unsigned __stdcall Thread::threadProc(void *param) {
35 Thread *p = static_cast<Thread*>(param);
36 if (p->_target != 0)
37 p->_target->run();
38 else
39 p->run();
40 return 0;
41 }

37 行, 如果被delete, 而且run访问了数据, 而不仅仅是打印一些console消息, 肯定会引发错误。


疑问是: join 是惯例? 规范? 还是仅仅是boost的示例而已。


2.Thread 似乎并不需要从 Runable 继承。
完全可以设计成 Thread是Runable的容器。

疑问是: 仅仅是为了模仿Java的行为?
  回复  更多评论   

# re: 从Win32 API封装Thread类[1] 2008-10-10 09:15 蚂蚁终结者

@Robinfoxnan
OwnWaterloo回答的也是我想回答的,我就不多说了。  回复  更多评论   

# re: 从Win32 API封装Thread类[1] 2008-10-10 09:35 蚂蚁终结者

@OwnWaterloo
建议:
1.单下划线开始的标识符?
这个是编程风格问题,很难说出哪个更好哪个更差,正所谓萝卜白菜各有所爱,个人认为成员变量无论是下划线开始(如:_handle)还是下划线结尾(如:handle_),又或者是用匈牙利命名法,只要在编码中始终保持一种风格就好。编码风格对程序员来说可能是一种信仰。

2.将windows.h 从thread.h中移走
这个确实是可以考虑,如果考虑到跨平台的话。

疑问:
1. Thread的析构
据我所知,join应该是等待线程结束的惯用法,Java.Thread,boost.Thread都通过join来保证线程的安全结束。是否是规范我也不清楚,貌似join是POSIX中线程取消规范中的一个?

2.Thread 似乎并不需要从 Runable 继承
是不需要继承Runnable的,这样只是模仿Java的行为。
  回复  更多评论   

# re: 从Win32 API封装Thread类[1][未登录] 2013-06-08 17:19 Jackie

这里有个问题, 是 如果你没有join的话, 线程对象就会被马上析构, 程序出现崩溃.
所以我把线程对象创建在堆上面了, 然后在Run调用完之后在threadProc里面delete 完成  回复  更多评论   


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