【发布时间】:2016-07-21 00:15:03
【问题描述】:
我有一个对象列表,每个对象都有由“更新”函数计算的成员变量。我想并行更新对象,即我想为每个对象创建一个线程来执行它的更新函数。
这是合理的做法吗?为什么这可能不是一个好主意的任何原因?
下面是一个尝试做我描述的程序,这是一个完整的程序,所以你应该能够运行它(我使用的是 VS2015)。目标是并行更新每个对象。问题是一旦更新函数完成,线程会抛出“会发生资源死锁”异常并中止。
我哪里错了?
#include <iostream>
#include <thread>
#include <vector>
#include <algorithm>
#include <thread>
#include <mutex>
#include <chrono>
class Object
{
public:
Object(int sleepTime, unsigned int id)
: m_pSleepTime(sleepTime), m_pId(id), m_pValue(0) {}
void update()
{
if (!isLocked()) // if an object is not locked
{
// create a thread to perform it's update
m_pThread.reset(new std::thread(&Object::_update, this));
}
}
unsigned int getId()
{
return m_pId;
}
unsigned int getValue()
{
return m_pValue;
}
bool isLocked()
{
bool mutexStatus = m_pMutex.try_lock();
if (mutexStatus) // if mutex is locked successfully (meaning it was unlocked)
{
m_pMutex.unlock();
return false;
}
else // if mutex is locked
{
return true;
}
}
private:
// private update function which actually does work
void _update()
{
m_pMutex.lock();
{
std::cout << "thread " << m_pId << " sleeping for " << m_pSleepTime << std::endl;
std::chrono::milliseconds duration(m_pSleepTime);
std::this_thread::sleep_for(duration);
m_pValue = m_pId * 10;
}
m_pMutex.unlock();
try
{
m_pThread->join();
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl; // throws "resource dead lock would occur"
}
}
unsigned int m_pSleepTime;
unsigned int m_pId;
unsigned int m_pValue;
std::mutex m_pMutex;
std::shared_ptr<std::thread> m_pThread; // store reference to thread so it doesn't go out of scope when update() returns
};
typedef std::shared_ptr<Object> ObjectPtr;
class ObjectManager
{
public:
ObjectManager()
: m_pNumObjects(0){}
void updateObjects()
{
for (int i = 0; i < m_pNumObjects; ++i)
{
m_pObjects[i]->update();
}
}
void removeObjectByIndex(int index)
{
m_pObjects.erase(m_pObjects.begin() + index);
}
void addObject(ObjectPtr objPtr)
{
m_pObjects.push_back(objPtr);
m_pNumObjects++;
}
ObjectPtr getObjectByIndex(unsigned int index)
{
return m_pObjects[index];
}
private:
std::vector<ObjectPtr> m_pObjects;
int m_pNumObjects;
};
void main()
{
int numObjects = 2;
// Generate sleep time for each object
std::vector<int> objectSleepTimes;
objectSleepTimes.reserve(numObjects);
for (int i = 0; i < numObjects; ++i)
objectSleepTimes.push_back(rand());
ObjectManager mgr;
// Create some objects
for (int i = 0; i < numObjects; ++i)
mgr.addObject(std::make_shared<Object>(objectSleepTimes[i], i));
// Print expected object completion order
// Sort from smallest to largest
std::sort(objectSleepTimes.begin(), objectSleepTimes.end());
for (int i = 0; i < numObjects; ++i)
std::cout << objectSleepTimes[i] << ", ";
std::cout << std::endl;
// Update objects
mgr.updateObjects();
int numCompleted = 0; // number of objects which finished updating
while (numCompleted != numObjects)
{
for (int i = 0; i < numObjects; ++i)
{
auto objectRef = mgr.getObjectByIndex(i);
if (!objectRef->isLocked()) // if object is not locked, it is finished updating
{
std::cout << "Object " << objectRef->getId() << " completed. Value = " << objectRef->getValue() << std::endl;
mgr.removeObjectByIndex(i);
numCompleted++;
}
}
}
system("pause");
}
【问题讨论】:
-
题外话:猿不杀猿,
main应返回int。这是不可违反的两条法律! -
看起来你有一个线程正在尝试加入自己。
-
离题:我看到没有
srand()的rand。不太确定这会做什么,但我怀疑它是否好。 -
“这不是一件好事”的原因是,正如您所见,它是完全不可预测的。也没有必要。如果你真的对这个东西进行了基准测试,你会发现它可能运行得相当慢,因为锁定的开销、创建线程来做事等等。 (我将此称为“燃烧的箭”方法:“为你必须做的每一件事向空中发射一支燃烧的箭,并相信他们会以某种方式解决所有问题。”他们不会......)
-
如果你想以一种可管理的方式做这种事情,创建一个 pool 的“worker”线程,它消耗一个线程安全的“to-do list”队列.将您想要更新的对象放在该队列中,然后是与线程数相等的多个“thank you, die now, please ...” 标记。每个线程等待从队列中删除一些东西,然后处理它并再次等待。它继续这样做,直到它出列“谢谢你,现在就死了”。工作池的大小与要处理的对象数量无关。
标签: c++ multithreading stdthread