Channels ▼
RSS

Parallel

Prefer Using Futures or Callbacks to Communicate Asynchronous Results


Option 2: Return a Future (Caller Converts "Pull" Into "Push")

The "pull" model is great for many uses, but the example caller we saw above is a must-stay-responsive GUI thread. That kind of caller certainly doesn't want to wait for the "future" on any GUI thread method, because responsive threads must not block or stop processing new events and messages. There are workarounds, but they're not ideal: For example, it's possible for the GUI thread to remember there's a "future" and check it on each event that gets processed, and to make sure it sees it soon enough it can generate extra timer events to be woken up just so it can check the "future" — but that seems (and is) a lot harder than it should be.

Given that a responsive thread like a GUI thread is already event-driven, ideally we would like it to be able to receive the result as a new queued event message that it can naturally respond to just like anything else.

Option 2 is to have the caller do the work to convert "pull" to "push." In this model, the callee still returns a "future" as in Option 1, and it's up to the caller turn it into a proactive result. How can the caller do that? One way is to launch a one-off asynchronous operation that just waits for the "future" and then generates a notification from the result. Here's an example:


// Example 2(a): Calling code, taking result as a future
// and converting it into an event-driven notification
class MyGUI {
public:
  // …

  // When the user clicks [Save]
  void OnSaveClick() {
    // …
    // … turn on saving icon, etc. …
    // …
    shared_future<bool> result;
    result = backgrounder.Save( filename );
    // queue up a continuation to notify ourselves of the
    // result once it's available
    async( [=] { SendSaveComplete( result.get() ); } );
  }
  void OnSaveComplete( bool returnedValue ) {
    // … turn off saving icon, etc. …
  }

The statement SendSaveComplete( result->get() ); does two things: First, it executes result->get(), which blocks if necessary to wait for the result to be available. Then, and only then, it calls SendSaveComplete; in this case, an asynchronous method that when executed ends up calling OnSaveComplete and passes the available result. (C++0x-specific note: Like promises, ordinary "futures" are intended to be unique and therefore not copyable, but are move-only, so again we use the shared_ptr workaround to enable copying the result into the lambda for later use.)

Option 2 Variant: ContinueWith

As written, the Example 2(a) code has two disadvantages:

  • First, it spins up (and ties up) a thread just to keep the asynchronous operation alive, which is potentially unnecessary because the first thing the asynchronous operation does is go idle until something happens.
  • Second, it incurs an extra wakeup, because when the "future" result is available, we need to wake up the asynchronous helper operation's thread and continue there.

Some threading platforms offer a "future-like" type that has a ContinueWith-style method to avoid this overhead; for example, see .NET's Task<T>. [3] The idea is that ContinueWith takes a continuation to be executed once the thread that fills the "future" makes the "future" ready, and the continuation can be executed on that same target thread.

Tacking the continuation onto the "future-generating" work itself with ContinueWith, rather than having to use yet another fresh async operation as in Example 2(a), lets us avoid both of the problems we just listed: We don't have to tie up an extra thread just to tack on some extra work to be done when the "future" is ready, and we don't have to perform a wakeup and context switch because the continuation can immediately run on the thread that fills the "future." For example:


// Example 2(b): Calling code, same as 2(a) except using
// ContinueWith method (if available)
class MyGUI {
public:
  // …

  // When the user clicks [Save]
  void OnSaveClick() {
    // …
    // … turn on saving icon, etc. …
    // …
    shared_future<bool> result;
    result = backgrounder.Save( filename );
    // queue up a continuation to notify ourselves of the
    // result once it's available
    result.ContinueWith( [=] 
      SendSaveComplete( result->get() );
    } </font>);
  }
  void OnSaveComplete( bool returnedValue ) {
    // … turn off saving icon, etc. …
  }

Prefer to use a ContinueWith style if it is available in your "futures library."


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video