Скажем, у меня есть функция, которую я вызываю в потоке пользовательского интерфейса:

IAsyncOperation<BitmapImage^>^ GetImage()
{
    return create_async([]
    {
        return create_task(GetStreamAsync())
            .then([] (Stream^ stream)
        {
            auto image = ref new BitmapImage();
            // set image properties.
            image.SetSourceAsync(stream);
            return image;
        });
    });
}

Я знаю, что внутренняя задача также будет выполняться в потоке пользовательского интерфейса, поскольку ответ на этот вопрос указывает на то, что задача, связанная с квартирой, по умолчанию продолжается в ее квартире.

Но когда запланировано выполнение тела create_task? Это происходит синхронно? Или поток пользовательского интерфейса ждет, пока у него появятся свободные циклы для обработки асинхронной логики?

1
Craig 22 Апр 2018 в 01:58

1 ответ

Лучший ответ

Давайте узнаем:

void ShowThread(std::string caller)
{
  std::string result{ caller + ": Current thread is " };
  result = result + std::to_string(GetCurrentThreadId());
  result = result + "\r\n";

  OutputDebugStringA(result.c_str());
}

using namespace Windows::Foundation;
using namespace Windows::Storage;

IAsyncOperation<IStorageItem^>^ GetFile()
{
  ShowThread("GetFile");
  return create_async([]
  {
    ShowThread("Inside create_async");
    return create_task(
      ApplicationData::Current->LocalFolder->TryGetItemAsync(L"foo"))
      .then([](IStorageItem^ item)
      {
        ShowThread("Inside .then");
        return item;
    });
  });
}

MainPage::MainPage()
{
  InitializeComponent();
  GetFile();
}

Это показывает что-то вроде этого:

GetFile: Current thread is 34100
Inside create_async: Current thread is 34100
Inside .then: Current thread is 34100

Вы можете видеть, что все они находятся в одном потоке - create_async немедленно вызывает свой аргумент (он не планирует его как задачу) и просто преобразует возвращаемое значение в IAsyncOperation<T>. Если вы хотите, чтобы продолжение не было в потоке пользовательского интерфейса, вы можете сделать это:

    return create_task(
      ApplicationData::Current->LocalFolder->TryGetItemAsync(L"foo"))
      .then([](IStorageItem^ item)
    {
      ShowThread("Inside .then");
      return item;
    }, task_continuation_context::use_arbitrary());

Затем PPL запустит продолжение в произвольном потоке.

2
Peter Torr - MSFT 23 Апр 2018 в 04:25