在多线程应用程序中使用智能指针
Using smart pointers within multi-threaded application
我需要执行一些计算,这取决于以下两个或更多步骤:
class A
{
public:
double step1() { return 2.5; }
};
class B
{
public:
double step2() { return 1.2; }
};
class Result
{
public:
Result(std::shared_ptr<A> _a, std::shared_ptr<B> _b) : a(_a), b(_b) {};
double getResult() { return a->step1() + b->step2(); }
private:
std::shared_ptr<A> a;
std::shared_ptr<B> b;
};
实际上,第 1 步和第 2 步需要多态行为,因此这些(共享)指针将指向“接口”class,但该细节在这里并不重要。
现在,getResult()
中的最终计算也需要多态行为,因此我创建了一个指向 Result
的(唯一)指针,创建了一个调用 getResult()
的 lambda,并传递了该 lambda像这样给我的主题:
void run_multi_threaded_calculation()
{
auto result = create_result_unique_ptr();
const int nThreads = 4;
std::vector<double> save(nThreads);
auto task = [&](int n) {
// Preprocessing before getResult()
save[n] = n * result->getResult();
};
std::vector<std::thread> threads;
threads.reserve(nThreads);
for (size_t i = 0; i < nThreads; ++i)
{
threads.push_back(std::thread(task, i));
}
for (auto& th : threads)
th.join();
for (const auto& s : save)
std::cout << s << '\n';
}
问题 1:我是否使用了正确的智能指针配置和 lambda 捕获,例如unique_ptr
到 Result
和 shared_ptr
到 A
和 B
?经过一些猜测并检查更改智能指针类型后,上述编译(但如果 Result
中的 a
和 b
是 unique_ptr
则不会编译),但我不是确定这是否是解决此问题的最佳方法。
问题 2: 如果我将 lambda 替换为等效的(或者我认为的)函数对象,那么我的代码无法编译(错误 C2661:'std::tuple<ResultFunctor,unsigned int>::tuple' : 没有重载函数需要 2 个参数)。智能指针是否缺少某些东西,或者线程的工作方式,或者我的函数对象定义可能存在一些问题?
相关更改如下:
class ResultFunctor
{
public:
ResultFunctor(std::unique_ptr<Result> _result, std::vector<double>& _save) : result(std::move(_result)), save(_save) {};
void operator() (int n) { save[n] = n * result->getResult(); }
private:
std::unique_ptr<Result> result;
std::vector<double>& save;
};
并替换以下行:
void run_multi_threaded_calculation()
{
// Other stuff is unchaged...
/*auto task = [&](int n) {
// Preprocessing before getResult()
save[n] = n * result->getResult();
};*/
auto task = ResultFunctor(std::move(result), save);
// other stuff is unchanged...
}
您遇到的部分问题是您绕过了 unique_ptr
。但是当你试图将它传递到 std::thread 时,你错过了一个 move
驻留在你的 ResultFunctor class 中的那个:
threads.push_back(std::thread(task, i));
如果您必须使用 unique_ptr,那么您很可能需要在 ResultFunctor 中添加一个 move c'tor:
class ResultFunctor
{
public:
ResultFunctor(std::unique_ptr<Result> _result, std::vector<double>& _save) : result(std::move(_result)), save(_save) {};
void operator() (int n) { save[n] = n * result->getResult(); }
// Move c'tor rough un-tested example
ResultFunctor(ResultFunctor&& rf) :
result(std::move(rf.result)),
save(rf.save)
{};
private:
std::unique_ptr<Result> result;
std::vector<double>& save;
};
这样您就可以将它移到 std::thread 的 c'tor 中,它采用 r 值 ref
threads.push_back(std::thread(std::move(task), i));
至少这是我能看到的主要问题,即使它不会导致您当前的错误。
注意:您的 lambda 与您的“等效”函子完全不同 class。您的 lambda 正在通过引用捕获。然而,这并不是真的那么安全,因为当你将它传递给线程时,REAL unique_ptr 可能会超出范围并销毁它!
所以这不是一个很好的解决方案。在 c++14 中,我相信你可以使用 move: How to capture a unique_ptr into a lambda expression? and https://isocpp.org/wiki/faq/cpp14-language#lambda-captures
捕获 unique_ptr
我需要执行一些计算,这取决于以下两个或更多步骤:
class A
{
public:
double step1() { return 2.5; }
};
class B
{
public:
double step2() { return 1.2; }
};
class Result
{
public:
Result(std::shared_ptr<A> _a, std::shared_ptr<B> _b) : a(_a), b(_b) {};
double getResult() { return a->step1() + b->step2(); }
private:
std::shared_ptr<A> a;
std::shared_ptr<B> b;
};
实际上,第 1 步和第 2 步需要多态行为,因此这些(共享)指针将指向“接口”class,但该细节在这里并不重要。
现在,getResult()
中的最终计算也需要多态行为,因此我创建了一个指向 Result
的(唯一)指针,创建了一个调用 getResult()
的 lambda,并传递了该 lambda像这样给我的主题:
void run_multi_threaded_calculation()
{
auto result = create_result_unique_ptr();
const int nThreads = 4;
std::vector<double> save(nThreads);
auto task = [&](int n) {
// Preprocessing before getResult()
save[n] = n * result->getResult();
};
std::vector<std::thread> threads;
threads.reserve(nThreads);
for (size_t i = 0; i < nThreads; ++i)
{
threads.push_back(std::thread(task, i));
}
for (auto& th : threads)
th.join();
for (const auto& s : save)
std::cout << s << '\n';
}
问题 1:我是否使用了正确的智能指针配置和 lambda 捕获,例如unique_ptr
到 Result
和 shared_ptr
到 A
和 B
?经过一些猜测并检查更改智能指针类型后,上述编译(但如果 Result
中的 a
和 b
是 unique_ptr
则不会编译),但我不是确定这是否是解决此问题的最佳方法。
问题 2: 如果我将 lambda 替换为等效的(或者我认为的)函数对象,那么我的代码无法编译(错误 C2661:'std::tuple<ResultFunctor,unsigned int>::tuple' : 没有重载函数需要 2 个参数)。智能指针是否缺少某些东西,或者线程的工作方式,或者我的函数对象定义可能存在一些问题?
相关更改如下:
class ResultFunctor
{
public:
ResultFunctor(std::unique_ptr<Result> _result, std::vector<double>& _save) : result(std::move(_result)), save(_save) {};
void operator() (int n) { save[n] = n * result->getResult(); }
private:
std::unique_ptr<Result> result;
std::vector<double>& save;
};
并替换以下行:
void run_multi_threaded_calculation()
{
// Other stuff is unchaged...
/*auto task = [&](int n) {
// Preprocessing before getResult()
save[n] = n * result->getResult();
};*/
auto task = ResultFunctor(std::move(result), save);
// other stuff is unchanged...
}
您遇到的部分问题是您绕过了 unique_ptr
。但是当你试图将它传递到 std::thread 时,你错过了一个 move
驻留在你的 ResultFunctor class 中的那个:
threads.push_back(std::thread(task, i));
如果您必须使用 unique_ptr,那么您很可能需要在 ResultFunctor 中添加一个 move c'tor:
class ResultFunctor
{
public:
ResultFunctor(std::unique_ptr<Result> _result, std::vector<double>& _save) : result(std::move(_result)), save(_save) {};
void operator() (int n) { save[n] = n * result->getResult(); }
// Move c'tor rough un-tested example
ResultFunctor(ResultFunctor&& rf) :
result(std::move(rf.result)),
save(rf.save)
{};
private:
std::unique_ptr<Result> result;
std::vector<double>& save;
};
这样您就可以将它移到 std::thread 的 c'tor 中,它采用 r 值 ref
threads.push_back(std::thread(std::move(task), i));
至少这是我能看到的主要问题,即使它不会导致您当前的错误。
注意:您的 lambda 与您的“等效”函子完全不同 class。您的 lambda 正在通过引用捕获。然而,这并不是真的那么安全,因为当你将它传递给线程时,REAL unique_ptr 可能会超出范围并销毁它!
所以这不是一个很好的解决方案。在 c++14 中,我相信你可以使用 move: How to capture a unique_ptr into a lambda expression? and https://isocpp.org/wiki/faq/cpp14-language#lambda-captures
捕获 unique_ptr