Мне удалось взять приведенный выше код и создать простое приложение, воспроизводящее эту проблему. Короче говоря, я смог заставить future.wait()
вернуться, сказав продолжение в asyncTaskExceptionHandler
для запуска в фоновом потоке:
template <typename T, typename CallbackLambda>
void asyncTaskExceptionHandler(concurrency::task<T> mainTask, CallbackLambda&& onResult) {
// debug
printString(mainTask.is_apartment_aware().ToString());
auto t1 = mainTask.then([onResult = std::move(onResult)](concurrency::task<T> t) {
bool didFail = true;
T result;
try {
result = t.get();
didFail = false;
}
catch (concurrency::task_canceled&) {
OutputDebugStringA("Win10 call was canceled.");
}
catch (Platform::Exception^ e) {
OutputDebugStringA("Error during a Win10 call:");
}
catch (std::exception&) {
OutputDebugStringA("There was a C++ exception during a Win10 call.");
}
catch (...) {
OutputDebugStringA("There was a generic exception during a Win10 call.");
}
// It works with this
}, concurrency::task_continuation_context::use_arbitrary());
}
Если предположить, что код, который я использовал, был верным, то, по моему мнению, произошло то, что мы создали тупик. То, что мы говорим с приведенным выше кодом:
- В потоке пользовательского интерфейса / STA создайте / обработайте асинхронную операцию с
GetFolderFromPathAsync
- Передайте эту задачу нашему
syncAsyncTask
, который, в свою очередь, передает это asyncTaskExceptionHandler
.
asyncTaskExceptionHandler
добавляет продолжение этой задачи, которое планирует его выполнение. По умолчанию задачи выполняются в потоке, который их вызвал. В данном случае это поток UI / STA!
- Как только поток запланирован, мы возвращаемся к
syncAsyncTask
для завершения После нашего вызова asyncTaskExceptionHandler
у нас есть future.wait()
блоков , пока значение обещания не будет установлено .
- Это не позволяет нашему потоку пользовательского интерфейса завершить выполнение
syncAsyncTask
, но также предотвращает запуск нашего продолжения, поскольку он запланирован для запуска в том же потоке, который блокирует!
Другими словами, мы ожидаем в потоке пользовательского интерфейса завершения операции, которая не может начаться до тех пор, пока поток пользовательского интерфейса не завершится, что приведет к нашей тупиковой ситуации.
Используя concurrency::task_continuation_context::use_arbitrary()
, мы сообщаем задаче, что можно использовать фоновый поток, если это необходимо (что в данном случае так и есть), и все завершается, как предполагалось.
Документация по этому вопросу, а также пример кода, иллюстрирующего асинхронное поведение, см. В Создание асинхронных операций в C ++ для приложений UWP. .