c++ - 使用 std::bind 中的 std::thread 和 std::function 以及带参数和非空返回的函数

标签 c++ multithreading c++11

假设我们有一个 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)=%s\n", 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()并让调用者处理参数的绑定(bind):

Live On Coliru

#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] 移出 vector ,那么为什么要保留死位 vector ?)
  • 根本没有安排;您只是随意创建新线程。这不会扩展(另外,您需要可插拔池模型;请参阅执行器规范和 Boost Async 对这些的实现)

更新

一个更简洁的版本,演示了可以摆脱哪些不需要的依赖:

  • 没有打包任务/线程的临时 vector
  • 没有假设/要求 std::function<>包装任务(这在实现内部删除了动态分配和虚拟分派(dispatch))
  • 不要求结果必须在 vector 中(事实上,您可以使用自定义输出迭代器在任何您想要的地方收集它们)
  • 移动意识(这可以说是代码的“复杂”部分,因为没有 std::move_transform ,所以使用 std::make_move_iterator 来加倍努力

Live On Coliru

#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容器:)

关于c++ - 使用 std::bind 中的 std::thread 和 std::function 以及带参数和非空返回的函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27953764/

相关文章:

c++ - 直接结构初始化

c++ - QWidget 的 paintEvent() 滞后应用程序

c++ - 类型错误 : unable to acces a wrapped function with double pointer argument

c++ - 找不到 Protocol Buffer 生成命令 libprotobuf.dll.a

c++ - Makefile导致编译错误

python - 使用线程 Ping 网络并测试它

c# - 多等待句柄

c++ - 如何在计算机上初始化一个 const 数组(制作 const 查找表)?

multithreading - Delphi线程返回值

c++ - 可变函数模板中的歧义