在UWP应用程序中,future.wait()在尝试同步异步方法的响应时保持等待状态

时间:2019-01-16 13:33:24

标签: c++ visual-studio asynchronous uwp windows-10

我正在开发一个UWP应用程序,单击Button可以从应用程序本地数据加载文件。为此,我需要使用StorageFolder方法为Application LocalFolder使用StorageFolder::GetFolderFromPathAsync()对象,然后我必须使用GetFileAsync()方法来读取要读取的StorageFile对象。

我已经编写了模板,以等待诸如GetFolderFromPathAsync(), GetFileAsync(),等异步方法的响应,然后再继续。

template <typename T>
T syncAsyncTask(concurrency::task<T> mainTask) {
    std::shared_ptr<std::promise<T>> done = std::make_shared<std::promise<T>>();
    auto future = done->get_future();

    asyncTaskExceptionHandler<T>(mainTask, [&done](bool didFail, T result) {
        done->set_value(didFail ? nullptr : result);
    });
    future.wait();
    return future.get();
}


template <typename T, typename CallbackLambda>
void asyncTaskExceptionHandler(concurrency::task<T> mainTask, CallbackLambda&& onResult) {
    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.");
        }
        onResult(didFail, result);
    });

}

问题:

  

当我调用syncAsyncTask()方法执行任何任务时   它的响应,它一直在future.wait()等待,因为mainTask从不   完成,promise从未设置其值。

     

请参见以下代码:

void testStorage::MainPage::Btn_Click(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
{
    Windows::Storage::StorageFolder^ localFolder = Windows::Storage::ApplicationData::Current->LocalFolder;
    auto task = concurrency::create_task(Windows::Storage::StorageFolder::GetFolderFromPathAsync(localFolder->Path));
    auto folder = syncAsyncTask<Windows::Storage::StorageFolder^>(task);
    printString(folder->Path);
}

void printString(Platform::String^ text) {
    std::wstring fooW(text->Begin());
    std::string fooA(fooW.begin(), fooW.end());
    const char* charStr = fooA.c_str();
    OutputDebugStringA(charStr);
}
  

运行环境:

     
      
  1. VS2017

  2.   
  3. 尝试过C ++ 14和C ++ 17,面临同样的问题。

  4.   
  5. Windows 10 RS5 Build#17763
  6.   

有人遇到过这个问题吗?

请帮助!!预先感谢。

1 个答案:

答案 0 :(得分:0)

我能够采用上面的代码,并创建了一个简单的应用程序来重现此问题。长话短说,我可以通过告诉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());
}

假设我使用的代码是正确的,我相信正在发生的事情是我们创建了一个死锁。我们在上面的代码中所说的是:

  1. 在UI / STA线程上,从GetFolderFromPathAsync创建/处理异步操作
  2. 将此任务传递给我们的syncAsyncTask,后者又将其传递给asyncTaskExceptionHandler
    • asyncTaskExceptionHandler向此任务添加延续,以安排其运行。默认情况下,任务在调用它们的线程上运行。在这种情况下,它是UI / STA线程!
  3. 线程被安排好之后,我们将返回到syncAsyncTask以完成操作。调用asyncTaskExceptionHandler后,我们有一个future.wait(),它会阻塞直到设置promise值
    • 这阻止了我们的UI线程完成syncAsyncTask的执行,但是 也阻止了我们的继续运行,因为它被安排为在阻塞的同一线程上运行!

换句话说,我们正在等待UI线程完成操作,该操作在UI线程完成之前无法开始,从而导致死锁。

通过使用concurrency::task_continuation_context::use_arbitrary(),我们告诉任务可以在必要时使用后台线程(在本例中为后台线程),并且一切都按预期完成。

有关此文档以及说明异步行为的示例代码,请参见Creating Asynchronous Operations in C++ for UWP Apps documentation