返回   cpper编程论坛 > 技术杂烩
注册账号 论坛帮助 会员列表 日历事件 搜索 今日新帖 标记版面已读

技术杂烩 找不到地方的技术问题?这里!

回复
 
LinkBack 主题工具 显示模式
  #1 (permalink)  
旧 2008-02-10
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 请教一下关于析构的问题……

我对线程做了个简单的封装,一个类从Active派生之后,调用它的activate函数就会激活一个线程来执行其中的activity函数。这是一个在Active里声明的纯虚函数:
private:
virtual void activity(void) =0;

但是现在遇到一个问题:如果我在桟上建立一个Active的派生类的实例,并且activate它,当桟把这个实例弹出时,会调用Active的析构函数,在这个析构函数里会调用join来等待线程结束。可是有时线程会开始的比较晚,一直到桟弹出时才开始执行,而这时这个实例已经在join等待状态了,于是在线程调用Active::activity()这个纯虚函数时,Active的派生类的那部分已经被析构了,activity的实现没了,这时线程就调用了那个纯虚函数,然后会输出:
pure virtual method called

This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.

虽然在桟弹出之前显示调用join能够解决这个问题,但是这依赖于使用者的记性,所以不是很好……
该怎么解决呢?

代码:
#include <boost/thread/thread.hpp> #include <boost/thread/recursive_mutex.hpp> using namespace boost; template< typename Message > class Active { public: Active() : _functor( *this ), _thread( NULL ) { } virtual ~Active() { join(); } public: void activate(void) { recursive_mutex::scoped_lock lock( _mutex ); if( !_thread ){ _thread= new thread( _functor ); } } void join(void) { recursive_mutex::scoped_lock lock( _mutex ); if( _thread ){ _thread->join(); delete _thread; _thread= NULL; } } void send( const Message& __msg ) { recursive_mutex::scoped_lock lock( _mutex ); } Message recv(void) { recursive_mutex::scoped_lock lock( _mutex ); } private: virtual void activity(void) =0; private: class Functor { public: Functor( Active& __active ) : _active( __active ) {} ~Functor() {} public: void operator () (void) { _active.activity(); } protected: Active& _active; }; private: Functor _functor; recursive_mutex _mutex; thread* _thread; };
代码:
#include "Active.h" class Loudspeaker : public Active< string > { public: void text( const string& __text ) { _text= __text; } private: virtual void activity(void) { for( int i= 0; i<5; ++i ){ printf( "%s", _text.c_str() ); //_sleep( 100 ); } } protected: string _text; }; int main( int argc, char* argv[] ) { Loudspeaker speaker[3]; speaker[0].text( "Test_0_!!\n" ); speaker[1].text( "Test_1_!!\n" ); speaker[2].text( "Test_2_!!\n" ); speaker[0].activate(); //_sleep( 100 ); //speaker[1].activate(); //_sleep( 100 ); //speaker[2].activate(); // speaker[0].join(); return 0; }
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #2 (permalink)  
旧 2008-02-11
Elminster 的头像
超级版主
 
注册日期: 2002-09-09
帖子: 1,763
Elminster 正向着好的方向发展
默认 回复: 请教一下关于析构的问题……

你这个设计恐怕走偏了,类似这样的问题从本质上来说是没法避免的。比如说某个 Active 的派生类这么实现:

代码:
class VectorPrinter : public Active< string > { private: vector<string> _v; VectorPrinter(const vector<string>& v) { copy(v.begin(), v.end(), back_inserter(_v)); } virtual void activity(void) { for (vector<string>::iterator i=_v.begin(); i!=_v.end(); ++i) { cout << *i << endl; _sleep(1000); } } };
看起来没太大问题吧?但如果你构造了一个 VectorPrinter 的对象并调用了它的 activate 函数,那么在对应的线程结束之前,你绝对不能析构这个 VectorPrinter 对象,即使你的基类析构函数中调用了 join 也一样:因为运行 VectorPrinter::activity() 函数的线程需要访问 VectorPrinter 中的 _v 成员,但在调用基类析构函数执行 join 之前,派生类的析构函数已经执行了,_v 成员已经被析构了。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #3 (permalink)  
旧 2008-02-12
polyrandom 的头像
超级版主
 
注册日期: 2002-09-03
帖子: 3,135
文章: 20
polyrandom 正向着好的方向发展
默认 回复: 请教一下关于析构的问题……

代码:
template <typename Base> class Active : public Base { void activate() { activity(); } void join() { /*blah*/ } void ~Active() { join(); } }; class MyClass { void activity() { /*aaaaaa*/ } }; void func() { Active<MyClass> mc; }
还有一种做法是,把Active本身作为一个handle,这样的话,可以保证原先的类体系,但是会损失一点效率(对线程来说,忽略不计)。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #4 (permalink)  
旧 2008-02-12
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教一下关于析构的问题……

引用:
作者: Elminster 查看帖子
你这个设计恐怕走偏了,类似这样的问题从本质上来说是没法避免的。比如说某个 Active 的派生类这么实现:

代码:
class VectorPrinter : public Active< string > { private: vector<string> _v; VectorPrinter(const vector<string>& v) { copy(v.begin(), v.end(), back_inserter(_v)); } virtual void activity(void) { for (vector<string>::iterator i=_v.begin(); i!=_v.end(); ++i) { cout << *i << endl; _sleep(1000); } } };
看起来没太大问题吧?但如果你构造了一个 VectorPrinter 的对象并调用了它的 activate 函数,那么在对应的线程结束之前,你绝对不能析构这个 VectorPrinter 对象,即使你的基类析构函数中调用了 join 也一样:因为运行 VectorPrinter::activity() 函数的线程需要访问 VectorPrinter 中的 _v 成员,但在调用基类析构函数执行 join 之前,派生类的析构函数已经执行了,_v 成员已经被析构了。
嗯,我遇到的就是这个问题。就是如何确保在线程结束之前对象不被析构。
最理想的情况是,Active的派生类无论怎么写,实际析构时都会首先执行Active的析构函数,并且不需要用户使用额外的辅助操作。不过现在感觉这不太可能实现了。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #5 (permalink)  
旧 2008-02-12
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教一下关于析构的问题……

引用:
作者: polyrandom 查看帖子
代码:
template <typename Base> class Active : public Base { void activate() { activity(); } void join() { /*blah*/ } void ~Active() { join(); } }; class MyClass { void activity() { /*aaaaaa*/ } }; void func() { Active<MyClass> mc; }
还有一种做法是,把Active本身作为一个handle,这样的话,可以保证原先的类体系,但是会损失一点效率(对线程来说,忽略不计)。
嗯,想了一下,大致理解你的意思了,还是RAII。唯一的遗憾是这样需要引入辅助类,而且对用户而言不是透明的。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #6 (permalink)  
旧 2008-02-13
polyrandom 的头像
超级版主
 
注册日期: 2002-09-03
帖子: 3,135
文章: 20
polyrandom 正向着好的方向发展
默认 回复: 请教一下关于析构的问题……

这并不是遗憾。类体系的用户分两种,第一种是对这个类体系进行扩展的人,第二种是直接使用这个类体系及其扩展的人。对前者来说,天生地他就需要知道这个类体系的部分细节。无论是ACE/MFC/STL/...,没有人可以说,我可以直接扩展,不用知道任何convention的。对于后者来说,一个typedef就可以解决所有的问题了。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #7 (permalink)  
旧 2008-02-13
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教一下关于析构的问题……

嗯……也是……
有时还是不得不相信使用者的智慧,嘿嘿
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #8 (permalink)  
旧 2008-02-16
liuxinyu 的头像
高级会员
 
注册日期: 2006-02-09
帖子: 303
文章: 48
liuxinyu 正向着好的方向发展
默认 回复: 请教一下关于析构的问题……

引用:
作者: polyrandom 查看帖子
代码:
template <typename Base> class Active : public Base
个人爱好,我比较喜欢这个方法,activity是一个policy。

另外也有一些其他策略,但不执着于RAII。
1. 首先,把Active的构造函数变成protected。这样任何人都不能自己构造任何Active对象在栈上。
2. 设计一ActiveManager,管理所有Active对象,其被声明为Active的friend
ActiveManager可以放到栈上,析构时遍历其管理的对象,如果IsRunning为true就join,否者直接干掉。
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
  #9 (permalink)  
旧 2008-02-16
sjinny 的头像
普通会员
 
注册日期: 2008-02-01
帖子: 66
sjinny 正向着好的方向发展
默认 回复: 请教一下关于析构的问题……

引用:
作者: liuxinyu 查看帖子
个人爱好,我比较喜欢这个方法,activity是一个policy。

另外也有一些其他策略,但不执着于RAII。
1. 首先,把Active的构造函数变成protected。这样任何人都不能自己构造任何Active对象在栈上。
2. 设计一ActiveManager,管理所有Active对象,其被声明为Active的friend
ActiveManager可以放到栈上,析构时遍历其管理的对象,如果IsRunning为true就join,否者直接干掉。
嗯,这个方案是可行的,但是这样就意味着只能使用Manager来管理,而不能简单直接地使用桟了。
我现在这样也是可以用Manager的,只不过不是强制的。
桟是个简单直接的管理工具,我不希望丢弃它而强迫用户使用更复杂的Manager。
^_^!
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
回复时引用此帖
回复

书签

主题工具
显示模式

发帖规则
不可以发表新主题
不可以发表回复
不可以上传附件
不可以编辑自己的帖子

启用 BB 代码
论坛启用 表情符号
论坛启用 [IMG] 代码
论坛禁用 HTML 代码
Trackbacks are 启用
Pingbacks are 启用
Refbacks are 启用



所有时间均为格林尼治时间 +9。现在的时间是 08:41 AM


Powered by vBulletin® 版本 3.7.0
版权所有 ©2000 - 2008,Jelsoft Enterprises Ltd.
(C) Copy Right All Right Reserved 2001 - 2007

Search Engine Friendly URLs by vBSEO 3.1.0