首页 > 解决方案 > 尝试创建线程的问题

问题描述

我正在尝试创建一个函数,该函数接收一个std::function和一个值向量,并将返回一个包含这些值的函数输出的向量,并使用线程来加速它。

当我编译我的代码时,它说它无法专门化函数模板std::invoke并且它需要 1 个参数并得到 7

这是我的代码:

#include <vector>
#include <thread>
#include <functional>
#include <iterator>

template<
    typename RETURN,
    typename INPUT
>
void thread_instance(std::function<RETURN(INPUT)> function, 
                     const std::vector<INPUT>& input, 
                     typename std::vector<INPUT>::iterator input_start, 
                     typename std::vector<INPUT>::iterator input_end,
                     std::vector<RETURN>& output,
                     typename std::vector<RETURN>::iterator output_start)
{
    for (; input_start != input_end; ++input_start, ++output_start)
    {
        *output_start = function(*input_start);
    }
}

template<
    typename RETURN,
    typename INPUT
>
std::vector<RETURN> thread_map(std::function<RETURN(INPUT)> function, std::vector<INPUT> input, int thread_count)
{
    std::vector<std::thread> threads(thread_count);
    std::vector<RETURN> output(input.size());
    for (int i = 0; i < thread_count; ++i)
    {
        int start_index = (input.size() / thread_count) * i;
        int end_index = start_index + input.size() / thread_count;
        typename std::vector<INPUT>::iterator thread_input_start = input.begin() + start_index;
        typename std::vector<INPUT>::iterator thread_input_end = input.begin() + end_index;
        typename std::vector<RETURN>::iterator thread_output_start = output.begin() + start_index;
        threads[i] = std::thread(thread_instance<RETURN, INPUT>, function, input, thread_input_start, thread_input_end, output, thread_output_start);
    }
    for (int i = 0; i < thread_count; ++i)
    {
        threads[i].join();
    }
    return output;
}

int multiply_by_2(int num)
{
    return num * 2;
}

int main(int argc, char** argv)
{
    std::vector<int> nums_to_sum = { 4,3,67,5,32,6,3,2,4 };
    std::vector<int> summed_nums = thread_map(std::function<int(int)>(multiply_by_2), nums_to_sum, 12);
}

标签: c++multithreadingtemplatesinvoke

解决方案


根据cppreferencestd::thread:_

线程函数的参数按移动或复制。如果需要将引用参数传递给线程函数,则必须对其进行包装(例如,使用std::refor std::cref)。

想想如果你用一个引用启动一个线程并且在线程启动后对象被销毁会发生什么?悬空引用和未定义的行为!因此,按值接受参数的默认行为是有意义的。

在您的情况下,您甚至没有使用输出向量。输出向量的迭代器就足够了,它可以按值传递。因此,我建议更改您的函数,使其不通过引用接受输出向量:

template<
    typename RETURN,
    typename INPUT
>
void thread_instance(std::function<RETURN(INPUT)> function,
    const std::vector<INPUT>& input,
    typename std::vector<INPUT>::iterator input_start,
    typename std::vector<INPUT>::iterator input_end,
    typename std::vector<RETURN>::iterator output_start)
{
    for (; input_start != input_end; ++input_start, ++output_start)
    {
        *output_start = function(*input_start);
    }
}

另一件事是,如果您使用整数除法计算起始迭代器,如下所示:

int start_index = (input.size() / thread_count) * i;

并且thread_count大于input.size()结果将始终为零。代替 12 的数字,例如 3,它是 9 的除数,会更好。还要记住,创建线程相对昂贵,您不想创建太多。std::thread::hardware_concurrency()将返回系统支持的并发线程数。


推荐阅读