求高手解答:C++必经之路 之不确定指针class Hello{public:void run(int c){do_sth(c) }}Hello* h new
求高手解答:C++必经之路 之不确定指针
class Hello
{
public:
void run(int c)
{ do_sth(c); }
};
Hello* h = new Hello;
Task* t = Bind(h, &Hello::run); 将类指针及类成员函数封装到一个Task类中
thread->PostTask(t); 将Task投递到线程
问题在:投递线程之后,如果h被delete了,此时线程去执行t的时候就会出错,因为h被删除了。
我的期望:在执行t的时候,先检查h是否还存在,不存在则丢弃该Task。
c++中有一个weak_ptr,弱指针。我知道这个可以解决这个问题,但是如何解决,尝试多次还是没有办法,特此请教高手?
[解决办法]
weak_ptr 构造之前,必须先完完全全构造 shared_ptr.
其实boost 提供了一个类:enable_share_from_this 完完全全解决了该问题。
[解决办法]
请不要怀疑人家的思路。这个思路是一个非常general的思路。
[解决办法]请不要怀疑人家的思路。这个思路是一个非常general的思路。
没有怀疑他的思路,只是越问我感觉越迷惑而已!
[解决办法]Proxy:
template <typename T>
class TPtrProxy
{
public:
TPtrProxy(T* raw_ptr): m_raw_ptr(raw_ptr) { }
void Release()
{
delete m_raw_ptr;
m_raw_ptr = 0;
}
T* Get() { return m_raw_ptr; }
T* m_raw_ptr;
};
class CTask { };
typedef TPtrProxy<CTask> CTaskProxy;
void foo(CTaskProxy task)
{
if (!task.Get())
{
cout << "task is invalid." << endl;
}
// TODO..
}
int main()
{
CTaskProxy task( new CTask );
task.Release();
foo(task);
#ifdef __LOCAL__
int i;
cin >> i;
#endif
return 0;
}
[解决办法]在多线程中这种模式不可用的....
首先你的CTaskProxy task( new CTask );要改成
CTaskProxy* task =new CTaskProxy( new CTask );
然后传递task到其他线程
他问的问题就反而成了
何以保证在我使用task的时候 task没有被删掉
[解决办法]我可以说 在多线程模式下 没有任何行为能够保证你的指针没有问题...
举个现实的例子
指针就好比 一张纸上写了我家里的地址,然后我把我家给炸掉了....
然后你不会知道我把家给炸掉了.....电脑没有异能,或者心灵感应...他也要遵循这个规律
然后你想你可以做什么,首先你可以找我问一下,这个地址还存不存在...但这首先就要保证我存在...
你也许说你按照那个地址的位置找过去,不需要任何保证,可那只是一个地址,你如何保证找到的东西就是我当时写纸上时候找个地址上的东西...
你可以说出你的想法 你的需要,也许有更好的办法来解决.....
为什么一定要进入一个死循环里面那......什么都是指针 又什么都想保证..... 这无疑是不可能的
不论怎么样都要有一些东西保证存在来维持一条锁链中的每一环
[解决办法]上次在听我们公司的一个讲座时也提了下这个。当时他给我们的回答时尽量不要去bind一个成员函数,而是bind一个全局函数。然后在这个全局函数里面再去判断窗口是否被删除了,再转发给窗口。
不过我还是喜欢绑定一个成员函数,不过我绑定的这个类也是一个全局类,这样就方便了。
[解决办法]delete h; 没有减少引用计数,所以s != NULL
wp.lock此时的行为 未定义。
[解决办法]#include <stdint.h>
#include <iostream>
#include <string>
#include "ace/Task.h"
#include "ace/Thread.h"
class CObject_
{
public:
CObject_()
{
m_guid=generateGuid();
}
uint32_t GUID()
{
return m_guid;
}
virtual ~CObject_()
{
}
static uint32_t generateGuid()
{
static ACE_Thread_Mutex mutex;
mutex.acquire();
uint32_t cur=++m_CurrentMaxGuid;
mutex.release();
return cur;
}
private:
static uint32_t m_CurrentMaxGuid;
uint32_t m_guid;
};
uint32_t CObject_::m_CurrentMaxGuid=0;
class CProto:public CObject_
{
public:
CProto(){}
void dowload(){printf("开始下载proto guid=%d",GUID());Sleep(1000*15);printf("下载成功 guid=%d",GUID());lllll=GUID();}
virtual ~CProto(){}
private:
int lllll;
};
typedef void (CProto::*DoFun)();
struct Task
{
public:
CProto* object;
DoFun fun;
};
class CDownloadProcess:public ACE_Task_Base
{
public:
CDownloadProcess():m_bstop(false)
{
}
Task* nectprocess()
{
m_mutex.acquire();
std::multimap<uint32_t,Task>::iterator itr=taskMapList.begin();
if(itr==taskMapList.end())
{
return NULL;
}
m_currentDownloadGuid=itr->second.object->GUID();
m_mutex.release();
return &(itr->second);
}
void AddObjectTodoFun(const Task&task)
{
m_mutex.acquire();
taskMapList.insert(std::make_pair(task.object->GUID(),task));
m_mutex.release();
}
bool DeleteObjectToDo(uint32_t guid)
{
if(m_currentDownloadGuid==guid)return false;
m_mutex.acquire();
taskMapList.erase(guid);
m_mutex.release();
return true;
}
void stop()
{
m_bstop=true;
}
virtual int svc(void)
{
while(!m_bstop)
{
Task* task=nectprocess();
((task->object)->*(task->fun))();
m_currentDownloadGuid=0;
}
return 0;
}
private:
uint32_t m_currentDownloadGuid;
bool m_bstop;
ACE_Thread_Mutex m_mutex;
std::multimap<uint32_t,Task> taskMapList;
};
bool haveMustDownloadProto()//输入准备创建几个这玩意
{
static int maxsize=15;
return (--maxsize)!=0;
}
int main()
{
std::vector<CProto*> CProtoList;
CDownloadProcess process;
while(haveMustDownloadProto())//假设从外部读入所有 要下载的 proto
{
CProto* proto=new CProto;
CProtoList.push_back(proto);
Task task;
task.object=proto;
task.fun=&(CProto::dowload);
process.AddObjectTodoFun(task);
}
printf("总共读入%d个proto\n",CProtoList.size());
std::string str;
while(std::cin>>str)//按Ctrl+C退出循环
{
printf("请输入要删除读入的第几个proto :");
uint32_t index;
scanf("%u",&index);
if(!process.DeleteObjectToDo(CProtoList[index]->GUID()))
{
printf("此proto正在下载,不可删除 删除失败.....按任意键继续,Ctrl+C 退出\n");
continue;
}
delete CProtoList[index];
CProtoList.erase(CProtoList.begin()+index);
printf("删除此proto成功\n");
}
std::vector<CProto*>::iterator itr=CProtoList.begin();
process.stop();
process.wait();
while(itr!=CProtoList.end())
{
delete (*itr);
itr=CProtoList.erase(itr);
}
return 0;
};
[解决办法]
#include <stdint.h>
#include <iostream>
#include <string>
#include "ace/Task.h"
#include "ace/Thread.h"
class CObject_
{
public:
CObject_()
{
m_guid=generateGuid();
}
uint32_t GUID()
{
return m_guid;
}
virtual ~CObject_()
{
}
static uint32_t generateGuid()
{
static ACE_Thread_Mutex mutex;
mutex.acquire();
uint32_t cur=++m_CurrentMaxGuid;
mutex.release();
return cur;
}
private:
static uint32_t m_CurrentMaxGuid;
uint32_t m_guid;
};
uint32_t CObject_::m_CurrentMaxGuid=0;
class CProto:public CObject_
{
public:
CProto(){}
void dowload(){printf("开始下载proto guid=%d",GUID());Sleep(1000*15);printf("下载成功 guid=%d",GUID());lllll=GUID();}
virtual ~CProto(){}
private:
int lllll;
};
typedef void (CProto::*DoFun)();
struct Task
{
public:
CProto* object;
DoFun fun;
};
class CDownloadProcess:public ACE_Task_Base
{
public:
CDownloadProcess():m_bstop(false)
{
}
Task* nectprocess()
{
m_mutex.acquire();
std::multimap<uint32_t,Task>::iterator itr=taskMapList.begin();
if(itr==taskMapList.end())
{
return NULL;
}
m_currentDownloadGuid=itr->second.object->GUID();
m_mutex.release();
return &(itr->second);
}
void AddObjectTodoFun(const Task&task)
{
m_mutex.acquire();
taskMapList.insert(std::make_pair(task.object->GUID(),task));
m_mutex.release();
}
bool DeleteObjectToDo(uint32_t guid)
{
if(m_currentDownloadGuid==guid)return false;
m_mutex.acquire();
taskMapList.erase(guid);
m_mutex.release();
return true;
}
void stop()
{
m_bstop=true;
}
virtual int svc(void)
{
while(!m_bstop)
{
Task* task=nectprocess();
((task->object)->*(task->fun))();
m_currentDownloadGuid=0;
}
return 0;
}
private:
uint32_t m_currentDownloadGuid;
bool m_bstop;
ACE_Thread_Mutex m_mutex;
std::multimap<uint32_t,Task> taskMapList;
};
bool haveMustDownloadProto()//输入准备创建几个这玩意
{
static int maxsize=15;
return (--maxsize)!=0;
}
int main()
{
std::vector<CProto*> CProtoList;
CDownloadProcess process;
while(haveMustDownloadProto())//假设从外部读入所有 要下载的 proto
{
CProto* proto=new CProto;
CProtoList.push_back(proto);
Task task;
task.object=proto;
task.fun=&(CProto::dowload);
process.AddObjectTodoFun(task);
}
printf("总共读入%d个proto\n",CProtoList.size());
std::string str;
while(std::cin>>str)//按Ctrl+C退出循环
{
printf("请输入要删除读入的第几个proto :");
uint32_t index;
scanf("%u",&index);
if(!process.DeleteObjectToDo(CProtoList[index]->GUID()))
{
printf("此proto正在下载,不可删除 删除失败.....按任意键继续,Ctrl+C 退出\n");
continue;
}
delete CProtoList[index];
CProtoList.erase(CProtoList.begin()+index);
printf("删除此proto成功\n");
}
std::vector<CProto*>::iterator itr=CProtoList.begin();
process.stop();
process.wait();
while(itr!=CProtoList.end())
{
delete (*itr);
itr=CProtoList.erase(itr);
}
return 0;
};
[解决办法]
weak_ptr 构造之前,必须先完完全全构造 shared_ptr.
其实boost 提供了一个类:enable_share_from_this 完完全全解决了该问题。
现在是不能用boost啊。
我这样写,
Hello* h = new Hello;
shared_ptr<Hello> sp(h);
weak_ptr<Hello> wp(sp);
delete h;
shared_ptr<Hello> s = wp.lock();
if(s)
s->run(5);
else
cout<<"not exist";
return 0;
结果很奇怪:s不为NULL,而且执行run也正常,只是执行完后无法退出控制台
既然把指针交给sp,最好就不要再动果指针了...这里sp存的raw_ptr果断野指针了。
不过sp的引用计数用在这个问题上不太自然,非要用的话:
threadfun里应该写if (sp.use_count() == 1) 如果mainthread里没做什么跌眼镜操作的话,这就说明已经为空啦。
[解决办法]学会用设计解决自己的困难,没经验就去积累。
既然是线程间通讯,还希望支持异步task cancel,那么标志位很重要,一个线程安全的set<task id,>标记某task id是否存在。
1,分配
Hello* h = new Hello;
h->id = gUniqueId++;
function Task t = Bind(h, &Hello::run); 将类指针及类成员函数封装到一个Task类中
lock();set.insert(h->id);unlock();
thread->PostTask(t); 将Task投递到线程
线程中要做的就是pop出一个function task, 然后task()调用进入到hello::run中,在run中你必须lock检查set中是否存在本task id,存在才能执行并且执行完毕需要删掉set中的记录(需要double check一次,可能执行完毕之后被异步删除),否则说明已经被异步删除了不必执行。
不过我建议用shared_ptr作为bind的第一个参数,否则你的run中会出现ugly的delete this代码或者之类的代码。
异步取消只要从lock并从set中抹掉这条记录就可以了。 uniqueID是uint64_t就可以保证不重复了。
另外,这种设计思想是基于消息来共享内存的,而不是基于内存来共享消息的,对设计更加友好,你可以体会一下其中的用意。