1、Thread类接口的定义
一个线程类无论具体执行什么任务,其基本的共性无非就是 :
创建并启动线程
停止线程
另外还有就是能睡,能等,能分离执行(有点拗口,后面再解释)
还有其他的可以继续加…
于是我们可以把线程抽象为:
class thread
{
public:
Thread();
virtual ~Thread();
int start (void * = NULL);
void stop();
void sleep (int);
void detach();
void * join();
bool joinable();
protected:
virtual void * run(void *) = 0;
private:
... //这里只介绍主要的方法,对于不同的库方法会有差异,不过都是大同小异。
};
- stop(),sleep()这2个看名字就知道,一个停止,一个休眠等待。
- start() 和run() 这2个是由区别的,执行 start() 会告诉系统创建一个新线程并就绪,但是并不一定马上运行,而是让系统选择一个合适的时间来调用 run() 来运行,这样就有了异步的可能。而 run() 的话就是将CPU腾出来立刻运行此线程,run() 可以用来确保线程的首次运行。
- 而 join() 和 detach() 这2个方法就显得更加难以理解了,且听我慢慢道来。
2、一个线程,总是会有下面2种状态之间的一种:
- joinable:可会合的
- detachable:分离的(不可会合的)
一个子线程被创建之后默认为 joinable ,在子线程终止之前,我们需要调用 join() 函数来将其与父线程会合,只有这样在子线程终止之后才能被摧毁,其所占有的资源(内存,端口等)才会被释放,否则会导致内存泄露。当然我们可以用 detach() 方法来将其设置为分离的,一个线程被分离之后将不再受我们的控制,可以想象成托管给了系统,当其被终止的时候会被马上摧毁。joinable() 方法可以用来检验当前是否为 joinable。
当然 join() 还被设置用来实现一个强大的功能--同步:
如果我们在主线程第2行调用了 join() ,那么在子线程终止之前,主线程会一直阻塞在第2行,这样就可以用来同步子线程和主线程了。
join() 可以用来实现同步,所以在程序终止之前,主进程会一直阻塞在 join() 处,就不能再发送消息了,只有等待子线程终止了才能发送消息,所以我们把 join() 放在return 0;之前,这样的话就OK了。当然我们还可以把 join() 改成 detach() ,这样的话放哪里都是可以的,代码略。
- thread thread1(getMsg);
- thread1.join();
- while(1)
- {
- ...............
- }
在把函数对象传入到线程构造函数中时,需要注意:如果传递了一个临时变量,c++编译器会将其解析为函数生命,而不是类型对象的定义,例如:
Std::threadmy_thread(test_thread());
这里相当于声明了一个带有一个参数名为my_thread并但会一个std::thread对象的函数。解决这个问题的四种方式:
1) 在前面命名函数对象;
2) 使用多组括号,std::thread my_thread((test_thread()));
3) 使用c++11新的初始化语法:std::thread my_thread{test_thread()};//大括号括起来;
4) 使用lambda表达式(允许使用一个可以捕获局部变量的局部函数)。
使用分离线程情况:一个程序执行多个相同的线程人物,每个线程运行相同的代码,但是每个线程都是独立的,完成与否对其他线程都不会产生影响。