免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
123下一页
最近访问板块 发新帖
查看: 11653 | 回复: 27
打印 上一主题 下一主题

C++封装pthread问题 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-11-02 15:57 |只看该作者 |倒序浏览
5可用积分
自己模仿java的线程类用c++封装了pthread,如下:
ThreadBase.h

  1. #ifndef __THREADBASE_H__
  2. #define __THREADBASE_H__

  3. #define THREAD_CALL
  4. typedef void* (*ThreadCall)( void* aArg );

  5. #include <pthread.h> //pthread API

  6. class ThreadBase
  7. {
  8. public:
  9.         ThreadBase( bool aDetached = false );
  10.         virtual ~ThreadBase();

  11.         bool Start();

  12.         pthread_t Id();
  13.         int ErrorCode();

  14.         void Join();
  15.         bool IsJoinable();

  16. protected:
  17.         virtual void Run(){};

  18.         pthread_t iId;
  19.         int           iErrorCode;
  20.         bool           iDetached; //用来标识线程是否分离

  21. private:
  22.         static void* THREAD_CALL ThreadFunc( void* aArg );
  23. };

  24. #endif /* __THREADBASE_H__ */
复制代码
ThreadBase.cpp

  1. #include <errno.h>
  2. #include <string.h>                //strerror

  3. #include "ThreadBase.h"

  4. ThreadBase::ThreadBase( bool aDetached )
  5.                    :iErrorCode( 0 ),
  6.                     iDetached( aDetached )
  7. {

  8. }

  9. ThreadBase::~ThreadBase()
  10. {
  11. }

  12. void* THREAD_CALL ThreadBase::ThreadFunc( void* aArg )
  13. {
  14.         ThreadBase* self = static_cast<ThreadBase*>( aArg );

  15.         self->Run();

  16.         //pthread_exit( (void*)0 );

  17.         return NULL;
  18. /*        return (void*)0;*/
  19. }

  20. bool ThreadBase::Start()
  21. {
  22.         if ( iDetached ) {
  23.                 pthread_attr_t attr;

  24.                 iErrorCode = pthread_attr_init( &attr );

  25.                 if ( 0 != iErrorCode )
  26.                         return false;

  27.                 iErrorCode = pthread_attr_setdetachstate( &attr, PTHREAD_CREATE_DETACHED );

  28.                 if ( 0 != iErrorCode ) {
  29.                         pthread_attr_destroy( &attr );
  30.                         return false;
  31.                 }

  32.                 iErrorCode = pthread_create( &iId, &attr, ThreadFunc, this );

  33.                 pthread_attr_destroy( &attr );
  34.         }

  35.         else
  36.                 iErrorCode = pthread_create( &iId, NULL, ThreadFunc, this );

  37.         return ( ( iErrorCode == 0 ) ? true : false );
  38. }

  39. pthread_t ThreadBase::Id()
  40. {
  41.         return iId;
  42. }

  43. int ThreadBase::ErrorCode()
  44. {
  45.         return iErrorCode;
  46. }

  47. bool ThreadBase::IsJoinable()
  48. {
  49.         return !iDetached;
  50. }

  51. void ThreadBase::Join()
  52. {
  53.         if ( !iDetached )
  54.                 pthread_join( iId, NULL );
  55. }
复制代码
子类Thread覆盖父类的Run方法:

  1. #include <stdlib.h>

  2. #include "ThreadBase.h"

  3. #include <iostream>

  4. using std::cerr;
  5. using std::endl;

  6. class Thread : public ThreadBase
  7. {
  8. public:
  9.         Thread( bool aDetached ):ThreadBase( aDetached ){}

  10. protected:
  11.         void Run();
  12. };

  13. void Thread::Run()
  14. {
  15.         cerr << "ID=" << iId << endl;
  16.         cerr << "Hello, world!" << endl;
  17. }
复制代码
在main方法中,以非分离状态启动线程之后,在main方法中调用sleep或线程对象的Join方法,线程运行正常:

  1. int main( int argc, char** argv )
  2. {
  3.         Thread thd( false );

  4.         thd.Start();

  5.         sleep( 1 );

  6.         return EXIT_SUCCESS;
  7. }
复制代码
或则:

  1. int main( int argc, char** argv )
  2. {
  3.         Thread thd( false );

  4.         thd.Start();

  5.         thd.Join();

  6.         return EXIT_SUCCESS;
  7. }
复制代码
若以分离状态启动线程,则必须在main方法中调用sleep函数,线程才能正常运行:

  1. int main( int argc, char** argv )
  2. {
  3.         Thread thd( true );

  4.         thd.Start();

  5.         sleep( 1 );

  6.         return EXIT_SUCCESS;
  7. }
复制代码
否则线程没有开始运行,进程已经退出,导致Run方法不能运行.
我在论坛上找到一篇帖子(详见:http://bbs.chinaunix.net/viewthread.php?tid=1650660),
有人说,如果以分离状态启动线程后,在main方法结尾去掉return,
调用pthread_exit( NULL ),线程能够正常运行.代码如下:

  1. #include <stdio.h>
  2. #include <pthread.h>

  3. void * thread_func(void * arg)
  4. {
  5.         pthread_t id = pthread_self();
  6.         printf("Thread 1 : %u start\n",id);
  7.         sleep(10);
  8.         fprintf(stderr,"Thread 1 :%u stop\n");
  9.         return NULL;
  10. }

  11. int main( int argc, char** argv )
  12. {
  13.     pthread_t thd1;
  14.     int ret;
  15.     pthread_attr_t attr;
  16.     pthread_attr_init(&attr);
  17.     pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
  18.     ret=pthread_create(&thd1,&attr,thread_func,NULL);
  19.     pthread_attr_destroy(&attr);
  20.     if(ret != 0)
  21.     {
  22.             printf("Create thread 1 error!\n");
  23.     }
  24.     printf("Thread 1 : %u\n",thd1);

  25.     pthread_exit( NULL );
  26. }
复制代码
我模仿以分离状态启动线程的代码如下:

  1. int main( int argc, char** argv )
  2. {
  3.         Thread thd( true );

  4.         thd.Start();

  5.         pthread_exit( NULL );
  6. }
复制代码
结果线程没有正常运行

为什么C的这种写法可以,而我的c++的就不行了?

是不是因为thd对象在主线程中创建,而主线程退出后把thd给销毁了?

大家知道原因是什么?或则有什么好的解决方案,谢谢!!!!

最佳答案

查看完整内容

答案不是你知道了吗?“是不是因为thd对象在主线程中创建,而主线程退出后把thd给销毁了”-->是的.对象是在栈上分配的,每个线程有一个独立的栈。main完成后,对象就over了,况且跨越了各自线程的栈的边界去访问其他栈就有可能导致不正确的行为。如果new一个对象的话,就不能释放这块内存了.-->可以清理的,用pthread_cleanup_push.具体,google一下

论坛徽章:
1
申猴
日期:2014-02-11 14:50:31
2 [报告]
发表于 2010-11-02 15:57 |只看该作者
答案不是你知道了吗?
“是不是因为thd对象在主线程中创建,而主线程退出后把thd给销毁了”
-->是的.对象是在栈上分配的,每个线程有一个独立的栈。main完成后,对象就over了,况且跨越了各自线程的栈的边界去访问其他栈就有可能导致不正确的行为。

如果new一个对象的话,就不能释放这块内存了.
-->可以清理的,用pthread_cleanup_push.
具体,google一下

论坛徽章:
1
申猴
日期:2014-02-11 14:50:31
3 [报告]
发表于 2010-11-02 17:19 |只看该作者
自己模仿java的线程类用c++封装了pthread,如下:
ThreadBase.hThreadBase.cpp子类Thread覆盖父类的Run方法: ...
osmanthusgfy 发表于 2010-11-02 15:57



    Thread thd( false );

放到全局里面试试

论坛徽章:
0
4 [报告]
发表于 2010-11-02 17:25 |只看该作者
谢谢!
这样是行,但还是没有解决更本问题.
可能是我的类的结构用问题,大家有什么好的方案?

论坛徽章:
1
申猴
日期:2014-02-11 14:50:31
5 [报告]
发表于 2010-11-02 17:39 |只看该作者
谢谢!
这样是行,但还是没有解决更本问题.
可能是我的类的结构用问题,大家有什么好的方案?
osmanthusgfy 发表于 2010-11-02 17:25



你想解决什么根本问题?
要么也可以new一个对象,而不是在栈分配。

论坛徽章:
0
6 [报告]
发表于 2010-11-02 17:43 |只看该作者
你想解决什么根本问题?
要么也可以new一个对象,而不是在栈分配。
chenzhanyiczy 发表于 2010-11-02 17:39


在main最后调用pthread_exit( NULL )后,处于分离状态的线程能够正常运行退出.

如果new一个对象的话,就不能释放这块内存了.

论坛徽章:
0
7 [报告]
发表于 2010-11-03 10:58 |只看该作者
谢谢!!!
pthread_cleanup_push/pthread_cleanup_pop是可以解决资源释放的问题.

但是这样导致接口不统一的问题:

1>.线程以分离状态启动,在堆上创建,子类必须覆盖父类清理线程的方法,再则,必须确保线程对象是在堆上创建的,也就是说,构造和析构不能为public;
2>.线程以joinable状态启动,在堆上创建和在栈上创建都可以.

所以导致线程清理的方法不能确定在什么条件下调用,我们不能假设线程对象是在堆上创建的还是在栈上创建的.
有一种解决方案:把构造和析构声明为private的,强制线程对象在堆上创建.但是这样又太霸道,也没有这个必要.

boost thread和zthread这两个库都没有实行这样的强制策略,并且接口也是统一的.

所以我想真正解决的,或则是想达到的目标是:
1>.屏蔽线程状态的区别(但是可以在初始化的时候选择线程状态),统一接口;
2>.封装pthread的细节,线程类对用户是透明的,并且在main函数中无需显示调用sleep和pthread(NULL)类似的函数等待子线程.

所以还有设计问题没有解决.继续希望高手给予帮助!
再次感谢chenzhanyiczy!!!

论坛徽章:
1
申猴
日期:2014-02-11 14:50:31
8 [报告]
发表于 2010-11-03 11:57 |只看该作者
1>.线程以分离状态启动,在堆上创建,子类必须覆盖父类清理线程的方法,再则,必须确保线程对象是在堆上创建的,也就是说,构造和析构不能为public;
--> ??,什么是堆上创建,构造和析构不能为public?
2>.线程以joinable状态启动,在堆上创建和在栈上创建都可以.
-->joinable状态的话,主线程需要pthread_join(),这样当然都是行的,因为主线程都没有退出,对象没有over。

“所以导致线程清理的方法不能确定在什么条件下调用,我们不能假设线程对象是在堆上创建的还是在栈上创建的.

-->栈上是不行的,堆的话,清理用pthread_cleanup_push。
The pthread_cleanup_push() function shall push the specified cancellation cleanup handler routine onto the calling thread's cancellation cleanup stack. The cancellation cleanup handler shall be popped from the cancellation cleanup stack and invoked with the argument arg when:

*
The thread exits (that is, calls pthread_exit()).
*
The thread acts upon a cancellation request.
*
The thread calls pthread_cleanup_pop() with a non-zero execute argument.
来自<linux man page>

2>.封装pthread的细节,线程类对用户是透明的,并且在main函数中无需显示调用sleep和pthread(NULL)类似的函数等待子线程.
-->你要明白,在栈上分配是不行的,除非全局和在堆上

论坛徽章:
0
9 [报告]
发表于 2010-11-03 13:38 |只看该作者
看来是我类封装本身存在的问题
大哥有没有其他的解决方案?

论坛徽章:
1
申猴
日期:2014-02-11 14:50:31
10 [报告]
发表于 2010-11-03 14:10 |只看该作者
看来是我类封装本身存在的问题
大哥有没有其他的解决方案?
osmanthusgfy 发表于 2010-11-03 13:38



你的类没错。
只是你没搞明白栈,堆创建对象和线程的关系,还有就是如何在线程结束时清理对象。这些之前的几个回答都有说明。
还是我没明白你的意思?
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP