30

Lines from Anthony William book:

std::launch::deferred indicates that the function call is to be deferred until either wait() or get() is called on the future.

X baz(X&); auto f7 = std::async(std::launch::deferred, baz, std::ref(x)); //run in wait() or get() //... f7.wait(); //invoke deferred function 

What could be the benefits or differences of this code over a direct call (baz(ref(x)) )?

In other words, what's the point of having future here?

2
  • perhaps codereview.stackoverflow.com is best suited for this question Commented Oct 9, 2017 at 6:44
  • 5
    @bradbury9 It's not about getting code reviewed, but about getting the concepts cleared by experts. :) Commented Oct 9, 2017 at 7:57

4 Answers 4

21

Suppose you have a thread pool.

The thread pool owns a certain number of threads. Say 10.

When you add tasks, they return a future, and they queue into the pool.

Threads in the pool wake up, grab a task, work on it.

What happens when you have 10 tasks in that pool waiting on a task later in the queue? Well, a deadlock.

Now, what if we return a deferred future from this pool.

When you wait on this deferred future it wakes up, checks if the task is done. If so, it finishes and returns.

Next, if the tasks is in the queue and not yet started, it steals the work from the queue and runs it right there, and returns.

Finally, if it is being run by the queue but not finished, it does something more complex. (the simplest version which usually works is that it blocks on the task, but that doesn't solve some pathological cases).

In any case, now if a task in the queue sleeps waits for another task in the queue to complete that isn't queue'd yet, we still get forward progress.


Another use of this is less arcane. Suppose we have some lazy values.

Instead of calculating them, we store shared futures with the calcuation steps in them. Now anyone who needs them just does a .get(). If the value has already been calculated, we get the value; otherwise, we calculate it, then get it.

Later, we add in a system to do some work on idle or in another thread. These replace said deferred lazy futures in some cases, but not in others.

Sign up to request clarification or add additional context in comments.

7 Comments

In your second example, you imply that get() can be called multiple times, but this does not seem to be the case - get() releases the shared state and valid() returns false afterward. This would have to be wrapped in a class that knows better.
@LB-- You need a shared future for multiple calls to get to work. "This is the case only for futures that were not default-constructed or moved from. Unlike std::future, std::shared_future's shared state is not invalidated when get() is called."
Ah, thanks for clarifying! I hadn't checked std::shared_future::get's documentation yet.
As for your second example: if you can store the calculation steps/result in a shared_future object, why do we store the result in a 'real' result object? We check if the object is null before using it. So, the first one that want to use it will do the actual calculation, and the following ones can just use it. I just don't see the necessity of using async here.
@WangTuma I have no idea how your question aligns with what I described in my second example; possibly because I don't know what you consider my second example. Are you referring to lazy values? Lazy values are highly useful. Suppose we have a presentation of data that is modestly expensive (thumbnail?). On selection change we invalidate it. When asked to display it, we lazily generate it. With eager generation, if we change selection 1000 times before display is requested (on idle) we waste 99.9% of work; with lazy, only the stuff humans see is generated.
|
14

I think, the main benefit is that it might be executed in a different thread - the one which actually reads the future. This allows to transfer 'units of work' between threads - i.e. thread 1 creates the future, while thread 2 calls wait on it.

3 Comments

To run in a different thread, we have to provide std::launch::async argument. As the book quotes, By default, it’s up to the implementation whether std::async starts a new thread, or whether the task runs synchronously when the future is waited for.
@SauravSahu, no, what I mean is that wait could be called in a thread which is different from the one creating a future.
@SauravSahu I think the distinction SergeyA is trying to make is that for std::launch:async the implementation supplies the thread that will preform the work while with std::launch::deferred you supply the thread (the one that reads the value).
0

in my point of view. I read effective modern c++ rule 35

Compared to thread-based programming, a task-based design spares you the travails of manual thread management 

it means std::launch::deferred is a worse case when the OS have no ability to allocate a new thread for you however, the baz function still work but it run as a deferred task instead of returning failed like pthread_create or throw exception with std::thread like this:

terminate called after throwing an instance of 'std::system_error' what(): Resource temporarily unavailable 

conclusion:

// same thread with called. std::async(std::launch::deferred, bax,..) = baz() // create a new thread to run baz(..) in case of OS have ability to allocate a new thread, otherwise same above std::async(baz, ...) = std::async(std::launch::deferred| std::launch::async , baz, ...) != baz() ; 

https://man7.org/linux/man-pages/man3/pthread_create.3p.html
tested at https://godbolt.org/z/hYv7TW51q

Comments

0

One cool thing that you're able to do, is to control on which thread the async task is ran.

Suppose that you want to limit the number of threads on your process. Starting several async jobs with async flag will make you lose any control you might have. Sometimes this is ok, but sometimes it's not.

But with deferred, you can start some task on the main thread, and on the worker thread(s) under your control, you might call get() to deterministically run the tasks.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.