How to poll an std::future to see if it's result is available?
-
martedì 17 aprile 2012 18:39
In the code snippet below I'm attempting to manually associate a promise set on one thread with a future checked on another. I then attempt to poll the future to see if it is ready but the loop never terminates. The call to future::wait_for() always returns future_status::deferred. If I remove the wait_for and instead loop for some fixed number of iterations with a sleep_for in the while loop I can get() the future as expected and the function exits normally.
Am I misunderstanding the correct usage of wait_for()? Is there any other way to poll for a future becoming available without blocking on a get() call?
#include <chrono>
#include <thread>
#include <future>
#include <iostream>
using namespace std;
using namespace std::chrono;void DoSomeWork(int numIters, promise<int>& ret) { int it = 0; for ( ; it < numIters; ++it) { cout << "Thread " << this_thread::get_id() << " working..." << endl; this_thread::sleep_for(milliseconds(1000)); } ret.set_value(it); } void TestPromise() { int its = 10; promise<int> aPromise; thread aThread(DoSomeWork, its, ref(aPromise)); auto aFuture = aPromise.get_future(); while (aFuture.wait_for(milliseconds(100)) != future_status::ready) { cout << "Thread " << this_thread::get_id() << " waiting..." << endl; } cout << "Work iterations: " << aFuture.get(); aThread.join(); }
int main()
{
TestPromise();
return 0;
}
Tutte le risposte
-
giovedì 19 aprile 2012 00:02I think .valid() is what you are looking for --- "checks if the result is available and not yet retrieved by get(), ..., returns true if available and false if not". Here's the documentation. Cheers!
-
giovedì 19 aprile 2012 00:37
I thought so too and that's what I tried initially but valid() doesn't do quite what I was looking for either. It always seems to return true immediately (before the promise has had its value set). What I was looking for is a way to query if the other thread has finished working and do some work while waiting for it to finish. If I run the following code though:
#include <chrono> #include <thread> #include <future> #include <iostream> void DoSomeWork(int numIters, promise<int>& ret) { int it = 0; for ( ; it < numIters; ++it) { cout << "Thread " << this_thread::get_id() << " working..." << endl; this_thread::sleep_for(milliseconds(100)); } cout << "DoSomeWork() finished doing some work." << endl; ret.set_value(it); } void TestPromise() { int its = 10; promise<int> aPromise; thread aThread(DoSomeWork, its, ref(aPromise)); auto aFuture = aPromise.get_future(); while (!aFuture.valid()) { cout << "Thread " << this_thread::get_id() << " waiting..." << endl; this_thread::sleep_for(milliseconds(100)); } cout << "aFuture.valid() returned true." << endl; cout << "Work iterations: " << aFuture.get() << endl; aThread.join(); } int main() { TestPromise(); return 0; }
I see this output:
Thread 5828 working... aFuture.valid() returned true. Thread 5828 working... Thread 5828 working... Thread 5828 working... Thread 5828 working... Thread 5828 working... Thread 5828 working... Thread 5828 working... Thread 5828 working... Thread 5828 working... DoSomeWork() finished doing some work. Work iterations: 10
Which is not what I was expecting.
-
venerdì 20 aprile 2012 04:54
Before I try the code myself, try using the Async function instead of creating the futures and promises yourself --- perhaps there is something wrong how you are using promises and futures. The Async function is also new in VC++ 11, and hides the thread for you as well as creates the promise and future for you (the promise is hidden, all you see is the future). So replace this
promise<int> aPromise;
thread aThread(DoSomeWork, its, ref(aPromise));
auto aFuture = aPromise.get_future();with code like this (the launch::async say always create a thread to do this work):
auto aFuture = Async(launch::async, DoSomeWork, its);
Then see if .valid() works, it really should...
- joe
-
venerdì 20 aprile 2012 06:43
Right, I thought using async with the launch::async flag should definitely work as well but when I tried it I got the same result:
#include <future> #include <thread> #include <iostream> using namespace std; using namespace std::chrono; int DoSomeWork(int numIters) { int count = 0; for (; count < numIters; ++count) { cout << "Thread " << this_thread::get_id() << " working..." << endl; this_thread::sleep_for(milliseconds(100)); } return count; } void TestAsync() { cout << "Main thread id: " << this_thread::get_id() << endl; cout << "Launching worker thread from main thread with async()" << endl; auto res = async(launch::async, DoSomeWork, 10); while (!res.valid()) { cout << "Thread " << this_thread::get_id() << " waiting..." << endl; this_thread::sleep_for(milliseconds(100)); } cout << "res.valid() returned true." << endl; cout << "res.get() = " << res.get(); } int main() { TestAsync(); return 0; }
Output:
Main thread id: 10952 Launching worker thread from main thread with async() res.valid() returned true. Thread 15144 working... Thread 15144 working... Thread 15144 working... Thread 15144 working... Thread 15144 working... Thread 15144 working... Thread 15144 working... Thread 15144 working... Thread 15144 working... Thread 15144 working... res.get() = 10
This behaviour seems wrong to me based on my reading of the standard - valid() should not return true until the result is available.
- Modificato mattnewport venerdì 20 aprile 2012 06:47 Added main thread id output
-
venerdì 20 aprile 2012 22:19
Okay, now I'm really suprised based on the online documentation, so I pasted the code into both VS11 and gcc 4.6. Identical to your tests --- so future::valid does not work as advertised. I finally did what I should have done --- grabbed my just-arrived copy of Anthony Williams "C++ Concurrency in Action" --- and read about .valid. Turns out future::valid is analogous to thread::joinable, returns true if the future is valid and false if not. Duh, I guess the method name makes more sense now :-) From AW's book, page 427:
"true if the *this has an associated async result, false otherwise"
Why would a future f not have an associated async result? Same as threads --- if you move it, the new owner is "valid", and the previous owner is not.
Okay, that clears up one mystery, but still leaves the original question: "how to poll for a thread instead of blocking?" As you noted earlier, future::wait_for doesn't work either...
I gotta run out the door (my daughter's birthday!), but I'm going to return to this, there *has* to be an answer...
-
sabato 21 aprile 2012 00:18
This is affected by internal bug number DevDiv#255669 "<future>: wait_for()/wait_until() don't block". Fortunately, I've received a fix for this from one of our Concurrency Runtime developers, Hong Hong. With my current build of VC11, this works:
C:\Temp>type meow.cpp #include <stdio.h> #include <chrono> #include <future> #include <thread> #include <windows.h> using namespace std; long long counter() { LARGE_INTEGER li; QueryPerformanceCounter(&li); return li.QuadPart; } long long frequency() { LARGE_INTEGER li; QueryPerformanceFrequency(&li); return li.QuadPart; } int main() { printf("%02d.%02d.%05d.%02d\n", _MSC_VER / 100, _MSC_VER % 100, _MSC_FULL_VER % 100000, _MSC_BUILD); future<int> f = async(launch::async, []() -> int { this_thread::sleep_for(chrono::milliseconds(250)); for (int i = 0; i < 5; ++i) { printf("Lambda: %d\n", i); this_thread::sleep_for(chrono::seconds(2)); } puts("Lambda: Returning."); return 1729; }); for (;;) { const auto fs = f.wait_for(chrono::seconds(0)); if (fs == future_status::deferred) { puts("Main thread: future_status::deferred (shouldn't happen, we used launch::async)"); } else if (fs == future_status::ready) { puts("Main thread: future_status::ready"); break; } else if (fs == future_status::timeout) { puts("Main thread: future_status::timeout"); } else { puts("Main thread: unknown future_status (UH OH)"); } this_thread::sleep_for(chrono::milliseconds(500)); } const long long start = counter(); const int n = f.get(); const long long finish = counter(); printf("Main thread: f.get() took %f microseconds to return %d.\n", (finish - start) * 1000000.0 / frequency(), n); } C:\Temp>cl /EHsc /nologo /W4 /MTd meow.cpp meow.cpp C:\Temp>meow 17.00.50419.00 Main thread: future_status::timeout Lambda: 0 Main thread: future_status::timeout Main thread: future_status::timeout Main thread: future_status::timeout Main thread: future_status::timeout Lambda: 1 Main thread: future_status::timeout Main thread: future_status::timeout Main thread: future_status::timeout Main thread: future_status::timeout Lambda: 2 Main thread: future_status::timeout Main thread: future_status::timeout Main thread: future_status::timeout Main thread: future_status::timeout Lambda: 3 Main thread: future_status::timeout Main thread: future_status::timeout Main thread: future_status::timeout Main thread: future_status::timeout Lambda: 4 Main thread: future_status::timeout Main thread: future_status::timeout Main thread: future_status::timeout Main thread: future_status::timeout Lambda: Returning. Main thread: future_status::ready Main thread: f.get() took 2.303971 microseconds to return 1729.I inserted timing code to prove that when wait_for() returns ready, f.get() returns instantly without blocking.
(Please note that I don't have time to monitor forum threads - I'm busy working on VC11's STL! - so if you want to bring something to my attention you should mail me at stl@microsoft.com .)
- Proposto come risposta DanielMothMicrosoft Employee, Owner sabato 21 aprile 2012 00:20
- Contrassegnato come risposta mattnewport sabato 21 aprile 2012 01:29
-
lunedì 25 giugno 2012 22:17
This appears to still be broken in the Visual Studio 2012 Release Candidate. Here's the output I see with the Release Candidate:
17.00.50522.01 Main thread: future_status::deferred (shouldn't happen, we used launch::async) Lambda: 0 Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Lambda: 1 Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Lambda: 2 Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Lambda: 3 Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Lambda: 4 Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Lambda: Returning. Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) Main thread: future_status::deferred (shouldn't happen, we used launch::async) ....
And the program never terminates. The version number printed at the beginning seems to be newer than the one in your build that had the fix. Was this a regression? Will this be fixed in the final release of Visual Studio 2012?
-
lunedì 25 giugno 2012 23:44
I can confirm that this fix didn't get into RC, but it'll be present in RTM. The fix got into Main long after RC had branched for release.
- Contrassegnato come risposta mattnewport martedì 26 giugno 2012 00:09
-
lunedì 20 agosto 2012 23:01
My original example still doesn't work in VS 2012 RTM. I can confirm that Stephan's example using async does work correctly now on RTM but my original example seems like it still does not behave correctly. The call to wait_for() returns future_status::deferred and so it loops forever.
In general using async is likely to be a better way of achieving the same result but it seems like my original example should work and that it does not work correctly in VS 2012 RTM.

