C++11 packaged_task running with its own thread need a join() in order to get future

2k views Asked by At

From the C++11 book I recently got, I am trying to build the example on page 123 (packaged_task 5.3.5.2) and after a few things to make this work with XCode, I have a couple of questions:

First, there seems to be a requirement to pass a function pointer to the packaged_task ctor. The text doesn't show the ampersand, so maybe it's a typo or a late standard change?

Then, and more important, I had to add thread.join(s) to be able to call get and avoid getting a sigabrt and the process exits without printing anything. I was under the impression that a call to get future would block till the thread returns a value, but this doesn't seem to be the case. Adding the join also seem to defy the asynchronous (non-blocking) nature of the code. Why can't get on the future do the join itself if it needs to? Reading from the docs, it says that a get will call implicitly wait and even by adding wait in the code, this still causes a sigabrt. This is very puzzling.

I am using c language dialect c11, -std=gnu++11 and libc++ (with c++11 support). I tried other options with no luck (when using gnuc++, it can't find ). This seems to be the only combinations I can use to build the code. XCode is very picky about what it needs to compile c++11.

Thanks for your help in advance.

Here is the test case from the book with my 'fixes' to make the code run:

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

    using namespace std;

    double accum(double *beg, double *end, double init)
    {
        return accumulate(beg, end, init);
    }

    double comp2(vector<double>& v)
    {

        using Task_type = double(double*,double*,double);

        packaged_task<Task_type> pt0 {&accum}; //use '&' to compile
        packaged_task<Task_type> pt1 {&accum}; //use '&' to compile

        future<double> f0 {pt0.get_future()};
        future<double> f1 {pt1.get_future()};

        double* first=&v[0];
        thread t1 {move(pt0), first, first+v.size()/2, 0};
        thread t2 {move(pt1), first+v.size()/2, first+v.size(), 0};

        // need this to run
        t1.join(); 
        t2.join();

        // using this instead of the join doesn't work
//      f0.wait(); 
//      f1.wait();

        return f0.get() + f1.get();
    }

    int main()
    {
        vector<double> v {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
        cout<<comp2(v)<<endl;
        return 0;
    }
3

There are 3 answers

1
Alan Stokes On BEST ANSWER

You need to either detach or join every thread object before destroying it. If you're using futures I'd expect the threads to have significantly longer lifetimes than the futures.

0
Charles Pehlivanian On

You need to call join() on the thread, or else detach it. Is that the question? The packaged_task may be used for exception safety, in this case, but you still have a pair of threads that run the show.

0
Zhengyang On

I think the current answers didn't answer the question directly. You don't need to explicitly create threads to run the tasks. For example:

#include <future>
#include <stdexcept>
#include <iostream>
#include <thread>
#include <utility>
#include <functional>

using namespace std;

int ff(int i)
{
  return i;
}

int main(int argc, char *argv[])
{

  packaged_task<int(int)> pt1 (ff);
  auto v1 = pt1.get_future();
  thread t1(move(pt1), 1);
  t1.join();

  packaged_task<int(int)> pt2(ff);
  auto v2 = pt2.get_future();
  pt2(1);
  cout << v2.get() << endl;

  return 0;
}

If you compile the above example you will see 1 as the stdout.

But what confused me is that, if I comment out the first packaged_task, then I see a std::system_error, the complete code is:

#include <future>
#include <stdexcept>
#include <iostream>
#include <thread>
#include <utility>
#include <functional>

using namespace std;

int ff(int i)
{
  return i;
}

int main(int argc, char *argv[])
{

  // packaged_task<int(int)> pt1 (ff);
  // auto v1 = pt1.get_future();
  // thread t1(move(pt1), 1);
  // t1.join();

  packaged_task<int(int)> pt2(ff);
  auto v2 = pt2.get_future();
  pt2(1);
  cout << v2.get() << endl;

  return 0;
}

Apparently the two packaged_tasks are independent and yet removing the first one will cause the second one to fail with a system_error.

This is not an answer to the question, but rather an attempt to clarify the question.