How to return value from nested task in c++/cx?

2019-08-08 17:20发布

I have a bunch of threaded tasks like this after each other:

create_task(somewinrtasyncfunction()).then([this(variable_that_the_last_task_returned)
{
    //task here
    return info;
}).then([this](info)
{
    //another task here
    return status});

Now I want to use status outside the tasks, in the function that called it. How would I access it?

1条回答
小情绪 Triste *
2楼-- · 2019-08-08 18:13

You return the task (or value) created by create_task(...).then(...).then(...).

If you need to get the result synchronously, you can try calling .get() on the task but that will not work on the main UI thread(s) of your application, and is not something you should do. The code might work on a background thread today, but you might end up calling the function on the UI thread in the future -- perhaps by some very round-about fashion -- and then your app will crash. The only way to fix the app will be to re-engineer your code to be asynchronous... like it should have been in the first place.

Also, note that trying to do your own work-arounds to get the value synchronously (like calling WaitForSingleObjectEx() on an object signaled in the task) can deadlock the UI thread for many WinRT Async methods. Don't do it!

You should continue the asynchronous chain to act on the value. Here is a simple example; call test() from your main UI thread.

concurrency::task<int> get_double_async(int value)
{
  return concurrency::create_task(
    [value]()
  {
    return value * 2;
  });
}

// DON'T DO THIS - IT IS A BUG FARM WAITING TO HAPPEN!
int try_calling_get()
{
  auto value = 2;
  value = get_double_async(value).get(); // TODO: Don't do this!
  value = get_double_async(value).get(); // TODO: Don't do this!
  return value;
}

concurrency::task<int> do_it_properly()
{
  auto value = 2;
  return get_double_async(value).then([](int value)
  {
    return get_double_async(value).then([](int value)
    {
      return value;
    });
  });
}

void test()
{
  wchar_t buff[255];
  swprintf_s(buff, L"test() being called on thread %d.\r\n", GetCurrentThreadId());
  OutputDebugString(buff);

  // this will fail at runtime if called from UI thread
  try
  {
    auto x = try_calling_get();
  }
  catch (const concurrency::invalid_operation& op)
  {
    // "Illegal to wait on a task in a Windows Runtime STA"
    swprintf_s(buff, L"try_calling_get() threw '%hs'; thread is %d.\r\n",
      op.what(), GetCurrentThreadId());
    OutputDebugString(buff);
  }

  // this will "work", but only because it is forced into a threadpool thread
  concurrency::create_task([]
  {
    auto x = try_calling_get(); // TODO: Don't do this!
    wchar_t buff[255];
    swprintf_s(buff, L"try_calling_get() returned %d; thread is %d.\r\n",
      x, GetCurrentThreadId());
    OutputDebugString(buff);
  });

  // this is the preferred way to do it
  do_it_properly().then([](int x)
  {
    wchar_t buff[255];
    swprintf_s(buff, L"do_it_properly() returned %d; thread is %d.\r\n",
      x, GetCurrentThreadId());
    OutputDebugString(buff);
  });
}

Note that this example uses value-based continuations .then([](int value){...}) rather than task-based continuations .then([](task<int> value){...}); you would use task-based continuations if you wanted control over things like exceptions.

Here's a sample run (the thread IDs will be different every time, and sometimes the last two will be the same)

test() being called on thread 3576.
First-chance exception at 0x77134598 in UniversalNativeApp.Windows.exe: Microsoft C++ exception: Concurrency::invalid_operation at memory location 0x038CEC94.
try_calling_get() threw 'Illegal to wait on a task in a Windows Runtime STA'; thread is 3576.
try_calling_get() returned 8; thread is 5972.
do_it_properly() returned 8; thread is 9976.
查看更多
登录 后发表回答