使用 std::thread 和 std::function 来自 std::bind 的函数,以及带有参数和非 voi
Using std::thread and std::function from a std::bind with a function with arguments and a non-void return
假设我们有一个函数odd
这是一个bool(int)
函数。我想并行执行这个函数,但使用不同的参数(不同的数字)。
bool odd(int i) { return (((i&1)==1)?true:false); }
这是我尝试使用的代码(有效但有疣)。
std::size_t num = 256;
std::vector<bool> results(num);
std::vector<std::function<bool(int)>> funcs(num);
std::vector<std::packaged_task<bool(int)>> tasks(num);
std::vector<std::future<bool>> futures(num);
std::vector<std::thread> threads(num);
for (std::size_t i = 0; i < num; i++) {
results[i] = false;
funcs[i] = std::bind(odd, static_cast<int>(i));
tasks[i] = std::packaged_task<bool(int)>(funcs[i]);
futures[i] = tasks[i].get_future();
threads[i] = std::thread(std::move(tasks[i]),0); // args ignored
}
for (std::size_t i = 0; i < num; i++) {
results[i] = futures[i].get();
threads[i].join();
}
for (std::size_t i = 0; i < num; i++) {
printf("odd(%d)=%sn", i, (results[i]?"true":"false"));
}
我想摆脱线程创建的参数,因为它们取决于函数bool(int)
的参数类型。我想制作此代码的函数模板,并能够制作一个庞大的并行函数执行器。
template <typename _returnType, typename ..._argTypes>
void exec_and_collect(std::vector<_returnType>& results,
std::vector<std::function<_returnType(_argTypes...)>> funcs) {
std::size_t numTasks = (funcs.size() > results.size() ? results.size() : funcs.size());
std::vector<std::packaged_task<_returnType(_argTypes...)>> tasks(numTasks);
std::vector<std::future<_returnType>> futures(numTasks);
std::vector<std::thread> threads(numTasks);
for (std::size_t h = 0; h < numTasks; h++) {
tasks[h] = std::packaged_task<_returnType(_argTypes...)>(funcs[h]);
futures[h] = tasks[h].get_future();
threads[h] = std::thread(std::move(tasks[h]), 0); // zero is a wart
}
// threads are now running, collect results
for (std::size_t h = 0; h < numTasks; h++) {
results[h] = futures[h].get();
threads[h].join();
}
}
然后像这样称呼:
std::size_t num = 8;
std::vector<bool> results(num);
std::vector<std::function<bool(int)>> funcs(num);
for (std::size_t i = 0; i < num; i++) {
funcs[i] = std::bind(odd, static_cast<int>(i));
}
exec_and_collect<bool,int>(results, funcs);
我会删除std::thread(std::move(task), 0);
行中的零,因为它完全被线程忽略了。如果我完全删除它,编译器找不到要传递给线程创建的参数,并且失败。
你不能只是在通用代码中成为微观管理/控制怪胎。只需returntype()
任何任务,并让调用者处理参数的绑定:
住在科里鲁
#include <thread>
#include <future>
#include <iostream>
#include <vector>
#include <functional>
bool odd(int i) { return (((i&1)==1)?true:false); }
template <typename _returnType>
void exec_and_collect(std::vector<_returnType>& results,
std::vector<std::function<_returnType()>> funcs
) {
std::size_t numTasks = std::min(funcs.size(), results.size());
std::vector<std::packaged_task<_returnType()>> tasks(numTasks);
std::vector<std::future<_returnType>> futures(numTasks);
std::vector<std::thread> threads(numTasks);
for (std::size_t h = 0; h < numTasks; h++) {
tasks[h] = std::packaged_task<_returnType()>(funcs[h]);
futures[h] = tasks[h].get_future();
threads[h] = std::thread(std::move(tasks[h]));
}
// threads are now running, collect results
for (std::size_t h = 0; h < numTasks; h++) {
results[h] = futures[h].get();
threads[h].join();
}
}
int main() {
std::size_t num = 8;
std::vector<bool> results(num);
std::vector<std::function<bool()>> funcs(num);
for (std::size_t i = 0; i < num; i++) {
funcs[i] = std::bind(odd, static_cast<int>(i));
}
exec_and_collect<bool>(results, funcs);
}
请注意,这是一项快速的工作,我在这里仍然看到很多过于具体的事情。
- 特别是所有临时集合都只是镇纸(您甚至在移动到下一个任务之前甚至将每个
tasks[h]
移出矢量,那么为什么要保留死位矢量呢? - 根本没有调度;你只是随意地创建新线程。这不会扩展(另外,你想要可插拔的池模型;请参阅执行器规范和Boost Async的实现)
更新
一个稍微清理的版本,演示了可以摆脱哪些不需要的依赖项:
- 没有打包任务/线程的临时向量
- 没有假设/要求
std::function<>
包装任务(这在实现中消除了内部的动态分配和虚拟调度) - 不需要结果必须在向量中(实际上,您可以使用自定义输出迭代器在任何地方收集它们)
- 移动意识(这可以说是代码的一个"复杂"部分,看到没有
std::move_transform
,所以使用std::make_move_iterator
加倍
努力
住在科里鲁
#include <thread>
#include <future>
#include <iostream>
#include <vector>
#include <algorithm>
#include <boost/range.hpp>
bool odd(int i) { return (((i&1)==1)?true:false); }
template <typename Range, typename OutIt>
void exec_and_collect(OutIt results, Range&& tasks) {
using namespace std;
using T = typename boost::range_value<Range>::type;
using R = decltype(declval<T>()());
auto tb = std::make_move_iterator(boost::begin(tasks)),
te = std::make_move_iterator(boost::end(tasks));
vector<future<R>> futures;
transform(
tb, te,
back_inserter(futures), [](auto&& t) {
std::packaged_task<R()> task(std::forward<decltype(t)>(t));
auto future = task.get_future();
thread(std::move(task)).detach();
return future;
});
// threads are now running, collect results
transform(begin(futures), end(futures), results, [](auto& fut) { return fut.get(); });
}
#include <boost/range/irange.hpp>
#include <boost/range/adaptors.hpp>
using namespace boost::adaptors;
int main() {
std::vector<bool> results;
exec_and_collect(
std::back_inserter(results),
boost::irange(0, 8) | transformed([](int i) { return [i] { return odd(i); }; })
);
std::copy(results.begin(), results.end(), std::ostream_iterator<bool>(std::cout << std::boolalpha, "; "));
}
输出
false; false; false; false; false; false; false; false;
请注意,您确实可以写
exec_and_collect(
std::ostream_iterator<bool>(std::cout << std::boolalpha, "; "),
boost::irange(0, 8) | transformed([](int i) { return [i] { return odd(i); }; })
);
并且没有任何results
容器:)
相关文章:
- 带有指定长度字符* 参数的 std::regex_search 在 VS2017 中不起作用?
- 为什么 std::function 可以作为 std::not2 的参数?
- 传递给std::function template的template参数究竟代表什么
- 转换函数,将 std::数组的双精度作为参数或双精度作为参数单独转换
- 在C++中,使用带有 std::optional 参数的函数<T>来表示可选参数是否有意义?
- 将函数参数"const char*"转换为"std::string_view"是
- 当指向对象的指针作为参数传递给 std::thread 时,内存可见性
- 为什么 std::绑定错误参数可以成功?
- 使用模板化的键类型定义 std::map,该键类型基于作为参数接收的函数
- std::vector 没有重载函数的实例与参数列表匹配
- std::span<const T> 作为函数模板中的参数
- SegFault 同时使用 std::string::operator+= 和函数作为参数
- 如果模板没有可变参数,则 Lambda 被推导出为 std::function
- 将参数打包的参数传递到 std::queue 中,以便稍后使用不同的函数调用
- 在构造函数中使用可变参数初始化 std::tuple
- 为什么我不能将引用作为 std::async 的函数参数传递
- 转发变量参数列表以模拟 std::thread
- 使用 std::enable_if 限制派生类的模板参数时出现编译错误
- "std::shared_ptr":不是参数"_Ty"的有效模板类型参数
- 可变参数模板参数扩展 类型为 std::function 的类成员