我正在开发一个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);
}
运行环境:
VS2017
尝试过C ++ 14和C ++ 17,面临同样的问题。
- Windows 10 RS5 Build#17763
有人遇到过这个问题吗?
请帮助!!预先感谢。
答案 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());
}
假设我使用的代码是正确的,我相信正在发生的事情是我们创建了一个死锁。我们在上面的代码中所说的是:
GetFolderFromPathAsync
创建/处理异步操作syncAsyncTask
,后者又将其传递给asyncTaskExceptionHandler
。
asyncTaskExceptionHandler
向此任务添加延续,以安排其运行。默认情况下,任务在调用它们的线程上运行。在这种情况下,它是UI / STA线程!syncAsyncTask
以完成操作。调用asyncTaskExceptionHandler
后,我们有一个future.wait()
,它会阻塞直到设置promise值。
syncAsyncTask
的执行,但是 也阻止了我们的继续运行,因为它被安排为在阻塞的同一线程上运行!换句话说,我们正在等待UI线程完成操作,该操作在UI线程完成之前无法开始,从而导致死锁。
通过使用concurrency::task_continuation_context::use_arbitrary()
,我们告诉任务可以在必要时使用后台线程(在本例中为后台线程),并且一切都按预期完成。
有关此文档以及说明异步行为的示例代码,请参见Creating Asynchronous Operations in C++ for UWP Apps documentation。