c++futurestdthreadpackaged-task

Why does my thread invoke a function object instead of assigning its return value to my packaged_task’s future object?


I want learn how to retrieve the return value of a function using a packaged_task.

In the code below I create a thread that runs my function DoTask. I then bind that function to packaged_task and get it to wait while I bind it to a packaged_task myTask. I extract myTask’s future object. Now I unblock the condition variable on DoTask (notify_one) to let it run to completion. I do a get() on myTask’s future to retrieve the value of the return bool in DoTask.

But instead of waiting for DoTask to return, DoTask enters code that does an “INVOKE a function object” and get() never gets executed.

What is keeping DoTask from setting the future I expected it to and instead invokes a function object?

#include "stdafx.h"
#include <future>
#include <memory>
#include <thread>
#include <condition_variable>
#include <mutex>

std::condition_variable notifyCondVar;
std::mutex mu;

bool DoTask() 
{
  {
    std::unique_lock< std::mutex > locker( mu );
    notifyCondVar.wait( locker );
  }
  return true;
}

int main()
{
  std::thread packageTaskThread( DoTask );
  std::packaged_task< bool() > myTask( std::bind( DoTask ) );
  std::future< bool > taskFuture = myTask.get_future();
  notifyCondVar.notify_one();
  bool okay = taskFuture.get();
  packageTaskThread.join();

  return 0;
}

Solution

  • You have a thread running DoTask, and a packaged_task instructed to run DoTask. You never actually executed the task, so the following two things are wrong:

    1. The thread will never terminate;
    2. The task's future will never be satisfied.

    Perhaps you meant to move-construct the thread from the packaged task, instead:

    #include <future>
    #include <memory>
    #include <thread>
    #include <condition_variable>
    #include <mutex>
    
    std::condition_variable notifyCondVar;
    std::mutex mu;
    
    bool DoTask() 
    {
      std::unique_lock<std::mutex> locker(mu);
      notifyCondVar.wait(locker);
    
      return true;
    }
    
    int main()
    {
      std::packaged_task<bool()> myTask([]() { return DoTask(); });
      std::future<bool> taskFuture = myTask.get_future();
    
      std::thread packageTaskThread(std::move(myTask));
    
      notifyCondVar.notify_one();
      bool okay = taskFuture.get();
    
      packageTaskThread.join();
    }
    

    You can see usage examples for std::packaged_task in this documentation.

    (BTW your extra scope block in DoTask is entirely redundant.)