Home > Software engineering >  Passing iterator of vector to the thread
Passing iterator of vector to the thread

Time:09-24

I have a trouble with passing begin() and end() iterators of vector to the thread. I would like to understand the mechanism of threads and I trying to do something like this:

#include <iostream>
#include <numeric>

double accum(double *beg, double *end)
{
    return std::accumulate(beg,end,0);
}

int main()
{
    std::vector<double> v;
    std::thread t1(accum,  &v.begin(), &v.end());
    t1.join();
    return 0;
}

I know that thread copy arguments and stores only rvalues - so here we should pass some wrapped pointer but how can I do this? Please explain me. I have already tried using & or std:: ref().

CodePudding user response:

Remove std::thread and make your code work without it. It should be either:

using iter = std::vector::const_iterator;

double accum( iter beg, iter end)
{
    return std::accumulate(beg,end,0);
}

std::vector<double> v;
accum( v.begin(), v.end() );

Or if you insist to use double * instead for some reason:

double accum(double *beg, double *end)
{
    return std::accumulate(beg,end,0);
}

std::vector<double> v;
accum( v.data(), v.data()   v.size() );

Now you can replace direct call with std::thread in either case. Your problem is not with std::thread, you just are trying to pass wrong arguments to your function.

CodePudding user response:

The ::begin() and ::end() functions of any container in the standard library are iterators to the beginning and end of the containers data. These are pointer-like constructs used for accessing the containers elements. The standard libraries functions are design to take these iterators directly (see the bottom for links to std::accumulate documentation).

Declaring a wrapper function such that std::thread can call it it the right thinking as dealing with std::accumulate template parameters explicitly (or any algorithm for that matter) can difficult and tricky to get right however, a typical function is not the desirable method. Instead what you want is a lambda that encapsulate the call to std::accumulate.

Because you're using threads, I also used atomics so the accumulated result was thread safe on assignment.

Just remember to link with -ltbb and -pthread.

eg.

#include <atomic>
#include <iostream>
#include <numeric>
#include <thread>
#include <vector>

int main()
{
    std::vector<double> v = {1, 2, 3, 4, 5, 6, 7, 8, 9};  // dummy data

    auto result {0};  // actual result
    std::atomic_ref<int> r { result };  // atomic reference to result

    // Captures atomic ref
    std::thread t1([&r](auto f, auto e, auto i) { r = std::accumulate(f, e, i); }
                   ,  v.begin(), v.end(), 0);
    t1.join();

    std::cout << r << '\n';

    return 0;
}

Alternative

Alternatively you can use the parallel version of accumulate, std::reduce with the std::execution policies but you'll need C 17. Don't forget to link with the same libraries as above.

#include <execution>
#include <iostream>
#include <numeric>
#include <vector>

namespace stdx = std::execution;

int main()
{
    std::vector<double> v = {1, 2, 3, 4, 5, 6, 7, 8, 9};

    auto r = std::reduce(stdx::par, v.begin(), v.end(), 0);

    std::cout << r << '\n';

    return 0;
}

Links

  • Related