谁能解释一下为什么此c++代码崩溃(实际上它不会崩溃,而是valgrind抱怨无效的内存访问)。
这个想法很简单。 Controller 会创建一些 worker 。每个工作人员都会得到函子对象,一旦完成工作,该对象将擦除该工作人员。谁能提出一些解决方案?
typedef boost::function<void()> Callback;
struct Worker
{
void start(Callback onFinnish)
{
// Called when finnished working
onFinnish();
}
};
typedef boost::shared_ptr<Worker> WorkerPtr;
struct Controler
{
void start()
{
for(int i = 0; i < 5; ++i)
{
auto workerPtr = boost::make_shared<Worker>();
workers.insert(workerPtr);
}
for(const auto &workerPtr: workers)
{
workerPtr->start(
[this, workerPtr] ()
{
workers.erase(workerPtr);
if(workers.size() == 0)
{
std::cout << "All workers done!" << std::endl;
}
}
);
}
}
std::set<WorkerPtr> workers;
};
// Somewhere in code
Controler c; c.start();
在评论后编辑:
我实现了这样的for auto循环,现在可以工作了:
for(auto workerIt = workers.begin(); workerIt != workers.end();)
{
auto nextWorker = workerIt; ++nextWorker;
(*workerIt)->start(
[this, workerIt] ()
{
workers.erase(workerIt);
if(workers.empty())
{
onWorkersDone();
}
}
);
workerIt = nextWorker;
}
最佳答案
在您的for范围内,您可以修改涉及UB的循环内的容器。
创建集合的副本应解决您的问题:
for(const auto &workerPtr: std::set<WorkerPtr>(workers)) // create a copy of the set
{
workerPtr->start(
[this, workerPtr] ()
{
workers.erase(workerPtr);
if(workers.size() == 0)
{
std::cout << "All workers done!" << std::endl;
}
}
);
}
编辑:
正如DietmarKühl所述,如果您的方法
start
启动一个新线程,那么您将在set
上进行数据竞争。