注意(更新):无需阅读整个文本墙..问题+解决方案在下面进一步。
我想一劳永逸地找出在创建多线程应用程序时一直遇到这些错误的原因。我正在削减很多代码以保持简单,所以如果你试图编译它就行不通。
这是一个排队系统。实体将分为5组。 例如,如果实体离开组3,则组3将从另一个优先级较低且尚未满的组中抢夺实体。
我现在所做的只是创建实体并将它们排队,这发生在主线程上。之后,我启动一个将删除20个实体的提升线程。这是读取访问冲突发生的地方,我无法弄清楚导致它的原因。你如何调试这样的东西?
main.h
//Externs, so all files can access the objects. Created in main.cpp .
#include "queue.h"
typedef std::map<unsigned int, Entity*> entityMap; //id,entity
extern entityMap entitymap;
typedef std::map<unsigned int, unsigned int> entityPositionMap; //Position,entityID
extern entityPositionMap entitypositionmap;
extern LFDGroups lfdgroups;
extern Queue queue;
的main.cpp
typedef std::map<unsigned int, Entity*> entityMap;
entityMap entitymap;
typedef std::map<unsigned int, unsigned int> entityPositionMap; //Position,id
entityPositionMap entitypositionmap;
LFDGroups lfdgroups;
Queue queue;
struct threadStarter2
{void operator()(){
for(unsigned int i = 0;i<20;i++) queue.removeFakeEntity();
}};
int main()
{
threadStarter1 startit1;
boost::thread thrd1(startit1);
whatNext(); //Prevent program exit
return 0;
}
queue.cpp
#include "main.h"
#include "queue.h"
Queue::Queue() //Lets add some entities and queue them at application start
{
for(unsigned int i = 0;i<20;i++){ addFakeEntity(false); }
//Queue all entities
std::map<unsigned int, Entity*>::iterator p;
for(p = entitymap.begin(); p != entitymap.end(); p++)
{
boost::mutex::scoped_lock lock(lfdgroups.access_groupsLookingForMore);
lfdgroups.addMember(p->second->id);
lock.unlock();
}
}
void Queue::addFakeEntity(bool queuenow)
{
Entity *entity = new Entity;
entity->id = entitymap.size();
entity->LFDGroupID = NULL;
entity->LFD = false;
entitymap.insert(std::pair<unsigned int,Entity*>(entity->id,entity));
if(queuenow)
{
entity->LFD = true;
boost::mutex::scoped_lock lock(lfdgroups.access_groupsLookingForMore);
lfdgroups.addMember(entity->id);
lock.unlock();
}
}
void Queue::removeFakeEntity()
{
//Remove random entity from random group
unsigned int groupID = getrand(0,lfdgroups.groupHeap.size()-1);
boost::mutex::scoped_lock lock(lfdgroups.access_groupsLookingForMore); //Thread safety
lfdgroups.groupHeap[groupID]->removeMember(lfdgroups.groupHeap[groupID]->arrayOfEntityIDs[getrand(0,lfdgroups.groupHeap[groupID]->arrayOfEntityIDs.size()-1)],true);
lock.unlock();
}
正如您所看到的,主线程目前没有做任何事情,并且提升线程正在逐个删除实体。
以下是删除权限(lfdgroups.groupHeap[groupID]->removeMember()
)
bool groupObject::removeMember(unsigned int entityID,bool snatchIt)
{
boost::mutex::scoped_lock lock(access_group);
for (unsigned int i = 0;i<arrayOfEntityIDs.size();i++)
{
if(arrayOfEntityIDs[i] == entityID) //Messy way of selecting the entity we are looking for.
{
std::stringstream ss;
ss.str(""); ss.clear(); ss << "Removed member " << entityID << " from group " << id << " which has " << groupSize() - 1 << " members left." << std::endl;
echo(ss.str());
arrayOfEntityIDs.erase(arrayOfEntityIDs.begin() + i);
if(snatchIt){ std::cout << "We have to snatch" << std::endl; snatch();}
else{ std::cout << "We don't have to snatch" << std::endl;}
return true;
}
}
lock.unlock();
return true;
}
snatchit设置为true,所以;
void groupObject::snatch()
{
boost::mutex::scoped_lock lock(snatching);
groupObject* thisgroup = NULL;
thisgroup = this;
if(!thisgroup) return;
std::cout << thisgroup->id << " need snatch? " << thisgroup->snatchCriteria() << std::endl;
if(!thisgroup->snatchCriteria()) return; //Do we even need to snatch a player (group < 5?) //This is a little redundant atm
std::map<unsigned int, unsigned int>::iterator p2;
for(p2 = entitypositionmap.begin(); p2 != entitypositionmap.end(); p2++)
{
Entity* entity = thisgroup->getEntity(p2->second);
if(entity != NULL && entity->LFDGroupID != thisgroup->id)
{
groupObject* targetgroup = NULL;
targetgroup = lfdgroups.getGroupObject(entity->LFDGroupID);
if(targetgroup != NULL && targetgroup->migrateCriteria())
{
lfdgroups.getGroupObject(thisgroup->id)->addMember(entity->id,false);
std::stringstream ss;
ss.str(""); ss.clear(); ss << "Snatched " << entity->id << " from " << targetgroup->id << " for: " << thisgroup->id << std::endl;
echo(ss.str());
break;
}
}
}
lock.unlock();
}
刚刚发生的事情是该小组检查了队列中的所有实体,直到找到一个可以抢夺的实体。之后lfdgroups.getGroupObject(thisgroup->id)->addMember(entity->id,false);
发生(所以在组对象中添加addmemeber,不要与lfdgroups的addmember混淆)。
bool groupObject::addMember(unsigned int entityID,bool snatchIt)
{
Entity* entity = getEntity(entityID);
if(entity == NULL) return false;
groupObject* group = lfdgroups.getGroupObject(entity->LFDGroupID);
if(group != NULL){ if(!group->removeMember(entityID,snatchIt)){return false;} }
if(elegibleCheck(entityID))
{
arrayOfEntityIDs.push_back(entityID);
entity->LFDGroupID = id;
std::stringstream ss;
ss.str(""); ss.clear(); ss << "Added member " << entityID << " to group " << id << " which has " << groupSize() << " members." << std::endl;
echo(ss.str());
return true;
}
return false;
}
所以我们刚刚从它的组中删除了实体,并将snatchIt设置为false以防止循环。之后我们只是将entity-&gt; LFDGroupID更新为当前组的id,这将是它的结尾。
很抱歉代码加载..并非所有代码都相关,但它允许您遵循remove-an-entity函数所采用的路径。我的主要问题是;如何快速调试访问冲突?
当应用程序崩溃时,断点最终会出现在名为“vector”
的文件中size_type size() const
{ // return length of sequence
return (this->_Mylast - this->_Myfirst); //Breakpoint here.
}
UPDATE(溶液):
我首先评论了Queue :: removeFakeEntity()函数的各个步骤,以找出它出错的地方。我立即发现问题出在lfdgroups.groupHeap[groupID]->removeMember(lfdgroups.groupHeap[groupID]->arrayOfEntityIDs[getrand(0,lfdgroups.groupHeap[groupID]->arrayOfEntityIDs.size()-1)],true);
所以我将它切成碎片
unsigned int entityID = lfdgroups.groupHeap[groupID]->arrayOfEntityIDs[getrand(0,lfdgroups.groupHeap[groupID]->arrayOfEntityIDs.size()-1)];
lfdgroups.groupHeap[groupID]->removeMember(entityID,true);
错误仍然存在。断点发生在矢量大小返回处,因此我将其隔离;
void Queue::removeFakeEntity()
{
std::cout << "size: " << lfdgroups.groupHeap[groupID]->arrayOfEntityIDs.size() << std::endl;
}
错误仍然。所以我尝试手动输入groupID。
void Queue::removeFakeEntity()
{
std::cout << "size: " << lfdgroups.groupHeap[1]->arrayOfEntityIDs.size() << std::endl;
std::cout << "size: " << lfdgroups.groupHeap[2]->arrayOfEntityIDs.size() << std::endl;
std::cout << "size: " << lfdgroups.groupHeap[4]->arrayOfEntityIDs.size() << std::endl;
}
工作得很好!它必须是groupID的一个问题。
void Queue::removeFakeEntity()
{
std::cout << "Heap size: " << lfdgroups.groupHeap.size() << std::endl;
}
这很奇怪,它显示'4'。但等一下!计算第一个元素(0)应该是5,对吧?
错了,我开始将groupID计为1,因为我想保留0作为返回值。我应该刚刚去了NULL。我昨天做出决定时甚至想过这件事。 “我会忘记我做到了!”为什么我不听自己?
问题在于unsigned int groupID = getrand(0,lfdgroups.groupHeap.size()-1);
,具有讽刺意味的是,这是我遇到问题的第一行功能......
答案 0 :(得分:1)
TL;博士
但是我敢打赌你的问题来自多次交易,即使你正在使用锁。
我的建议是替换
typedef std::map<unsigned int, Entity*> entityMap;
带
typedef std::map<unsigned int, EntityPtr> entityMap;
其中EntityPtr
是智能指针(您可以使用Boost
,因为您已经在使用它了。)
你可能在不同的线程中释放相同的内存。