【问题标题】:How to wait for an IAsyncAction?如何等待 IAsyncAction?
【发布时间】:2013-11-11 22:44:39
【问题描述】:

在 Windows 应用商店应用中,C++(C# 类似),类似

IAsyncAction^ Action = CurrentAppSimulator::ReloadSimulatorAsync(proxyFile);
create_task( Action ).then([this]()
{
}.wait();

导致未处理的异常。通常是

Microsoft C++ exception: Concurrency::invalid_operation at memory location 0x0531eb58

在尝试使用之前,我需要完成该操作以获取我的应用内购买信息。 这里奇怪的是,除了 IAsyncAction 之外的其他任何东西都可以正常等待。 IAsyncOperation 和 IAsyncOperationWithProgress 工作得很好,但是这个?异常然后崩溃。

说实话,我不知道 IAsyncOperation 和 IAsyncAction 之间有什么区别,它们看起来和我很相似。

更新

通过分析此页面http://msdn.microsoft.com/en-us/library/vstudio/hh750082.aspx,您可以发现 IAsyncAction 只是一个没有返回类型的 IAsyncOperation。但是,您可以看到大多数 IAsyncAction-s 是可等待的。但真正的问题是某些 Windows 函数只想在特定线程上执行(出于某种原因)。 ReloadSimulatorAsync 就是一个很好的例子。

使用这样的代码:

void WaitForAsync( IAsyncAction ^A )
{   
    while(A->Status == AsyncStatus::Started)
    {
        std::chrono::milliseconds milis( 1 );
        std::this_thread::sleep_for( milis );
    }   
    AsyncStatus S = A->Status;  
}

导致无限循环。如果调用其他功能,它实际上可以工作。这里的问题是,如果一切都是 Async ,为什么需要在特定线程上执行任务?它应该是 RunOn(Main/UI)Thread 或类似的,而不是 Async。

已解决,查看答案;

【问题讨论】:

  • 我相信 IAsyncOperation 返回一个结果,而 IAsyncAction 没有。我不知道 IAsyncAction 是否可以等待,说实话。在 C# 中,可等待异步方法返回类型 Task 或 Task。你能等Action吗?我不知道 C++ 是否有 async/await。
  • 我正在等待 "}.wait();"部分,这是 await C# 等价物,所以是的 IAsyncAction 是可等待的。另一件奇怪的事情是,我可以调用 task 而不是 create_task 并且具有抛出异常的相同效果。
  • 请注意:task::wait 不等同于 C# 的 await 关键字。 await 导致编译器将代码重组为幕后的延续。
  • 有任何参考资料吗?我认为这会让事情变得非常难以理解。 MessageDialog::ShowAsync(例如)只能从 UI 线程调用。在您描述的场景中,如果当您认为您在 UI 线程上时它在后台线程上执行,它将导致崩溃/异常。
  • 致在 IAsyncAction 上寻找 GetAwaiter() 的 C# 程序员 - 您需要从 c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\ 添加using System; 和对System.Runtime.WindowsRuntime.dll 的引用.NETCore\v4.5\ 或类似的。

标签: windows-runtime winrt-async


【解决方案1】:

在创建它之后在concurrency::task 上调用wait 完全违背了首先拥有任务的意义。

您必须意识到,在 Windows 运行时,有许多异步操作不能(或不应该)在 UI 线程上运行(或等待);你找到了其中一个,现在你正试图等待它。你得到的不是潜在的死锁,而是一个异常。

要解决这个问题,您需要使用a continuation。你大部分都在那儿;你已经定义了一个延续函数:

IAsyncAction^ Action = CurrentAppSimulator::ReloadSimulatorAsync(proxyFile);
create_task( Action ).then([this]()
{
}).wait();

// do important things once the simulator has reloaded
important_things();

...但你没有使用它。您传递给then 的函数的目的是在任务完成后从UI 线程调用。因此,您应该这样做:

IAsyncAction^ Action = CurrentAppSimulator::ReloadSimulatorAsync(proxyFile);
create_task( Action ).then([this]()
{
    // do important things once the simulator has reloaded
    important_things();
});

在任务完成之前,您重要的重新加载后代码不会运行,它将在后台线程上运行,因此不会阻塞或死锁 UI。

【讨论】:

  • 我猜你是对的。如果有一个 ReloadSimulatorSync 函数,我会调用它。不幸的是,在 Windows 8 Runtime 中,一切都是异步的。我对您的解决方案的问题是 1)我正在使用 #ifdef USE_STORE_SIMULATOR,因此只有在使用模拟器时才会调用任务中的所有代码和 2 个其他任务,代码在 #else 部分变得相当不可读。 2)依赖于此的代码在其他类中,我必须移动十几个类/函数才能适应这种异步范式。当你的整个程序是一个巨大的延续链时,这很烦人。
  • 你说得对,这是同步编程的一大飞跃。但是,不幸的是,对于您的代码,该平台的构建方式需要您考虑异步性。反过来,这将导致您在整个代码库中传播异步友好的代码。微软看到了这一点,这就是为什么他们将asyncawait 运算符嵌入到C# 中,以使这种重构更容易。不幸的是,这种简便性无法在 C++/CX 中复制。
  • 我不会说“看到这个”。我想说他们考虑了使用多个 CPU 内核的最简单方法,而无需为具有 2 个以上内核的设备编写特定的多线程代码。这是否使 Windows 8/RT 比其他平台更快?并不真地。这会惹恼开发人员吗?是的。我要指出的一件事是,它完全破坏了简单函数中的任何 CPU 缓存,而且大部分时间延续/任务都很短。
  • 我认为让 Windows 运行时在已经可用的后台线程(如 I/O 完成端口)上执行异步延续可以抵消您可能从缓存未命中看到的任何性能损失。
【解决方案2】:

这是完成工作的神奇修复:

void WaitForAsync( IAsyncAction ^A )
{   
    while(A->Status == Windows::Foundation::AsyncStatus::Started)
    {   
        CoreWindow::GetForCurrentThread()->Dispatcher->ProcessEvents(CoreProcessEventsOption::ProcessAllIfPresent);                     
    }

    Windows::Foundation::AsyncStatus S = A->Status; 
}

【讨论】:

  • 嵌套调用ProcessEvents()安全吗?第一个叫我们,第二个是我们叫的。
【解决方案3】:

一般来说,您应该使用延续 (.then(...)),就像亚当的回答所说的那样,而不是阻塞。但是假设您出于某种原因想要等待(为了测试一些代码?),您可以从最后一个延续触发一个事件(使用 C# 用语):

    TEST_METHOD(AsyncOnThreadPoolUsingEvent)
    {

        std::shared_ptr<Concurrency::event> _completed = std::make_shared<Concurrency::event>();


        int i;

        auto workItem = ref new WorkItemHandler(
            [_completed, &i](Windows::Foundation::IAsyncAction^ workItem)
        {

            Windows::Storage::StorageFolder^ _picturesLibrary = Windows::Storage::KnownFolders::PicturesLibrary;

            Concurrency::task<Windows::Storage::StorageFile^> _getFileObjectTask(_picturesLibrary->GetFileAsync(L"art.bmp"));

            auto _task2 = _getFileObjectTask.then([_completed, &i](Windows::Storage::StorageFile^ file)
            {

                i = 90210;

                _completed->set();

            });

        });

        auto asyncAction = ThreadPool::RunAsync(workItem);

        _completed->wait();


        int j = i;

    }

顺便说一句,由于某种原因,此方法在 Visual Studio 测试结束后会导致异常。不过,我也已经在应用程序中对其进行了测试,并且没有问题。我不太确定测试有什么问题。

如果有人想要 C++/Wrl 示例,那么我也有。

2017 年 7 月 8 日更新:此处要求提供 C++/Wrl 示例。我刚刚在 Visual Studio 2017 的通用 Windows (10) 测试项目中运行了它。这里的关键是奇怪的部分 Callback&lt;Implements&lt; RuntimeClassFlags&lt;ClassicCom &gt;, IWorkItemHandler , FtmBase &gt;&gt; ,而不仅仅是 Callback&lt;IWorkItemHandler&gt; 。当我有后者时,程序卡住了,除了它在 .exe 项目中时。我在这里找到了这个解决方案:https://social.msdn.microsoft.com/Forums/windowsapps/en-US/ef6f84f6-ad4d-44f0-a107-3922d56662e6/thread-pool-task-blocking-ui-thread。有关详细信息,请参阅“敏捷对象”。

#include "pch.h"
#include "CppUnitTest.h"
#include <Windows.Foundation.h>
#include <wrl\wrappers\corewrappers.h>
#include <wrl\client.h>
#include <wrl/event.h>
#include <memory>
#include "concrt.h"
#include <Windows.System.Threading.h>


using namespace Microsoft::VisualStudio::CppUnitTestFramework;
using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
using namespace Windows::System::Threading;

using namespace ABI::Windows::Foundation;
using namespace ABI::Windows::System::Threading;



namespace TestWinRtAsync10
{
    TEST_CLASS(UnitTest1)
    {
    public:

        TEST_METHOD(AsyncOnThreadPoolUsingEvent10Wrl)
        {
            HRESULT hr = BasicThreadpoolTestWithAgileCallback();

            Assert::AreEqual(hr, S_OK);
        }

        HRESULT BasicThreadpoolTestWithAgileCallback()
        {

            std::shared_ptr<Concurrency::event> _completed = std::make_shared<Concurrency::event>();

            ComPtr<ABI::Windows::System::Threading::IThreadPoolStatics> _threadPool;
            HRESULT hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_System_Threading_ThreadPool).Get(), &_threadPool);

            ComPtr<IAsyncAction> asyncAction;
            hr = _threadPool->RunAsync(Callback<Implements<RuntimeClassFlags<ClassicCom>, IWorkItemHandler, FtmBase>>([&_completed](IAsyncAction* asyncAction) -> HRESULT
            {

                // Prints a message in debug run of this test
                std::ostringstream ss;
                ss << "Threadpool work item running.\n";
                std::string _string = ss.str();
                std::wstring stemp = std::wstring(_string.begin(), _string.end());
                OutputDebugString(stemp.c_str());
                // 
                _completed->set();

                return S_OK;

            }).Get(), &asyncAction);

            _completed->wait();

            return S_OK;
        }

    };
}

2017 年 8 月 8 日更新:更多示例,每个 cmets。

#include "pch.h"
#include "CppUnitTest.h"

#include <wrl\wrappers\corewrappers.h>
#include <wrl\client.h>
#include <wrl/event.h>
#include <memory>
#include "concrt.h"
#include <Windows.System.Threading.h>


#include <Windows.ApplicationModel.Core.h>

using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;

namespace TestWinRtAsync10
{
    TEST_CLASS(TestWinRtAsync_WrlAsyncTesting)
    {

    public:

        TEST_METHOD(PackageClassTest)
        {
            ComPtr<ABI::Windows::ApplicationModel::IPackageStatics> _pPackageStatics;

            HRESULT hr = GetActivationFactory(Microsoft::WRL::Wrappers::HStringReference(RuntimeClass_Windows_ApplicationModel_Package).Get(), &_pPackageStatics);

            ComPtr<ABI::Windows::ApplicationModel::IPackage> _pIPackage;

            hr = _pPackageStatics->get_Current(&_pIPackage);

            ComPtr<ABI::Windows::ApplicationModel::IPackage3> _pIPackage3;

            hr = _pIPackage->QueryInterface(__uuidof(ABI::Windows::ApplicationModel::IPackage3), &_pIPackage3);

            ComPtr<__FIAsyncOperation_1___FIVectorView_1_Windows__CApplicationModel__CCore__CAppListEntry> _pAsyncOperation;

            hr = _pIPackage3->GetAppListEntriesAsync(&_pAsyncOperation);

            std::shared_ptr<Concurrency::event> _completed = std::make_shared<Concurrency::event>();

            _pAsyncOperation->put_Completed(Microsoft::WRL::Callback<Implements<RuntimeClassFlags<ClassicCom>, ABI::Windows::Foundation::IAsyncOperationCompletedHandler <__FIVectorView_1_Windows__CApplicationModel__CCore__CAppListEntry*>, FtmBase >>
                ([&_completed](ABI::Windows::Foundation::IAsyncOperation<__FIVectorView_1_Windows__CApplicationModel__CCore__CAppListEntry*>* pHandler, AsyncStatus status)
            {
                __FIVectorView_1_Windows__CApplicationModel__CCore__CAppListEntry* _pResults = nullptr;

                HRESULT hr = pHandler->GetResults(&_pResults);

                ComPtr<ABI::Windows::ApplicationModel::Core::IAppListEntry> _pIAppListEntry;

                unsigned int _actual;

                hr = _pResults->GetMany(0, 1, &_pIAppListEntry, &_actual);

                ComPtr<ABI::Windows::ApplicationModel::IAppDisplayInfo> _pDisplayInfo;

                hr = _pIAppListEntry->get_DisplayInfo(&_pDisplayInfo);

                Microsoft::WRL::Wrappers::HString _HStrDisplayName;

                hr =  _pDisplayInfo->get_DisplayName(_HStrDisplayName.GetAddressOf());


                const wchar_t * _pWchar_displayName = _HStrDisplayName.GetRawBuffer(&_actual);


                OutputDebugString(_pWchar_displayName);


                _completed->set();

                return hr;



            }).Get());

            _completed->wait();

        };

    };
}

这个输出:

TestWinRtAsync10

【讨论】:

  • 嗨,您能分享一下您在回答中提到的 C++/Wrl 示例吗?
  • @SahilSingh 完成。
  • 据我了解IAgileObject,您的代码使用FtmBase,但问题是__FIVectorView_1_Windows__CApplicationModel__CCore__CAppList‌​Entry_t,我用来从WinRT API 获取数据的类(GetAppListEntriesAsync() 来自@987654331 @class),没有实现 IInspectable,因此 Visual Studio 会抛出以下错误。 Error C2338 'I' has to derive from 'IWeakReference', 'IWeakReferenceSource' or 'IInspectable' AsyncTask c:\sw\tools\sdk\winsdk\win10\include\winrt\wrl\implements.h 413
  • @SahilSingh 我没有关注您的最后评论,因此我尝试使用您提到的类和 API 并添加了另一个示例。我因大量使用智能感知而蒙混过关,它正在发挥作用。顺便说一句,所有 Windows 运行时接口都继承自 IInspectable 接口。
【解决方案4】:

以防万一有人在这里需要 C++/WinRT 中的解决方案。假设你有函数ProcessFeedAsync() 会返回IAsyncAction,只需要下面的简单代码:

winrt::init_apartment();

auto processOp{ ProcessFeedAsync() };
// do other work while the feed is being printed.
processOp.get(); // no more work to do; call get() so that we see the printout before the application exits.

source

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-12-15
    • 1970-01-01
    • 1970-01-01
    • 2021-08-07
    • 2021-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多