Вопрос по windows-runtime, winrt-async – Как ждать IAsyncAction?

9

В приложениях Магазина 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 или аналогичный.

РЕШИТЬсм. ответ;

Просто примечание:task::wait не является эквивалентом C #await ключевое слово.await заставляет компилятор реструктурировать код в продолжения за кулисами. Adam Maras
Я жду с "} .wait ();" часть, это ожидаемый C # эквивалент, так что да, IAsyncAction является ожидаемым. Еще одна странная вещь заключается в том, что вместо create_task я могу вызывать задачу <void> и вызывать то же самое исключение. RelativeGames
Чтобы программисты на C # искали GetAwaiter () на IAsyncAction - вам нужно добавитьusing System; и ссылка наSystem.Runtime.WindowsRuntime.dll из c: \ Program Files (x86) \ Справочные сборки \ Microsoft \ Framework \ .NETCore \ v4.5 \ или аналогичные. kjhf
Есть какие-либо ссылки на это? Я думаю, что это усложнит задачу. MessageDialog :: ShowAsync (например) вызывается только из потока пользовательского интерфейса. В сценарии, который вы описываете, если он выполняется в фоновом потоке, когда вы думаете, что находитесь в потоке пользовательского интерфейса, это вызовет сбой / исключение. RelativeGames
Я считаю, что IAsyncOperation возвращает результат, а IAsyncAction - нет. Я не знаю, является ли IAsyncAction ожидаемым или нет, по правде говоря. В C # ожидаемые асинхронные методы возвращают тип Task или Task <TResult>. Можете ли вы ждатьAction? Я не знаю, есть ли в C ++ async / await. Nate Diamond

Ваш Ответ

4   ответа
0

если кому-то понадобится, это решение на 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.

источник

4

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

    Windows::Foundation::AsyncStatus S = A->Status; 
}
Безопасно ли делать вложенный вызовProcessEvents()? Первый звал нас, а второй звали мы. Alex P.
4

wait наconcurrency::task после того, как вы его создадите, он полностью побеждает необходимость ставить задачи на первое место.

Вы должны понять, что в среде выполнения Windows есть много асинхронных операций, которые нельзя (или не следует) запускать (или ожидать) в потоке пользовательского интерфейса; Вы нашли один из них, и теперь вы пытаетесь ждать его. Вместо того, чтобы потенциально вызвать тупик, вы получаете исключение.

Чтобы исправить это, вам нужно использоватьпродолжение, Вы большую часть пути там; вы уже определяете функцию продолжения:

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

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

... но ты не используешь это. Цель функции, которую вы передаетеthen должен называтьсявне пользовательского интерфейса как только задача завершена. Итак, вместо этого вы должны сделать это:

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

Ваш важный код после перезагрузки не будет работать до тех пор, пока задача не будет завершена, и он будет работать в фоновом потоке, чтобы не блокировать и не блокировать пользовательский интерфейс.

Я бы сказал, что разрешение Windows Runtime выполнить асинхронное продолжение в уже доступном фоновом потоке (например, порт завершения ввода / вывода) сводит на нет любое снижение производительности, которое вы можете увидеть из-за промаха кэша. Adam Maras
Я бы не сказал «видел, что это идет». Я бы сказал, что они подумали о самом простом способе использования нескольких процессорных ядер без написания специального многопоточного кода для устройств с 2+ ядрами. И делает ли это Windows 8 / RT быстрее, чем другие платформы? На самом деле, нет. Разве это раздражает разработчиков? Да. Одна вещь, на которую я хотел бы обратить внимание, это то, что она полностью уничтожает любое кэширование процессора простыми функциями, и в большинстве случаев продолжения / задачи довольно короткие. RelativeGames
Вы правы, это большой скачок от синхронного программирования. Но, к сожалению для вашего кода, платформа построена таким образом, что вы должны учитывать асинхронность. В свою очередь, это заставит вас распространять асинхронный дружественный код по всей вашей кодовой базе. Microsoft увидела это, поэтому они испеклиasync а такжеawait операторы в C #, чтобы сделать этот вид рефакторинга проще. К сожалению, эта простота не может быть воспроизведена в C ++ / CX. Adam Maras
Я полагаю, вы правы. Если бы была функция ReloadSimulatorSync, я бы это назвал. К сожалению, в Windows 8 Runtime все работает в асинхронном режиме. Моя проблема с вашим решением заключается в том, что 1) я использую #ifdef USE_STORE_SIMULATOR, поэтому весь этот код и 2 другие задачи в задачах вызываются ТОЛЬКО при использовании симулятора, код становится довольно нечитаемым для части #else. 2) Код, который зависит от этого, находится внутри других классов, я должен был бы переместить дюжину классов / функций, чтобы вписаться в эту асинхронную парадигму. Это раздражает, когда вся ваша программа представляет собой огромную цепочку продолжений. RelativeGames
2

вы должны использовать продолжения (.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;

    }

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

Если кто-то хочет пример C ++ / Wrl, то у меня тоже есть.

Обновление 08/07/2017: По запросу здесь приведен пример C ++ / Wrl. Я только что выполнил это в тестовом проекте Universal Windows (10) в Visual Studio 2017. Главное здесь - это странная частьCallback<Implements< RuntimeClassFlags<ClassicCom >, IWorkItemHandler , FtmBase >> в отличие от всегоCallback<IWorkItemHandler> , Когда у меня было последнее, программа заклинило, за исключением случаев, когда это было в проекте .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;
        }

    };
}

Обновление 08/08/2017: Больше примеров, согласно комментариям.

#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

@SahilSingh Готово. Elliot
Как я понимаю дляIAgileObject ваш код используетFtmBase, но проблема в том, что__FIVectorView_1_Windows__CApplicationModel__CCore__CAppList‌​Entry_tкласс, который я использую для получения данных из WinRT API (GetAppListEntriesAsync() изpackage класс), не реализует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 Sahil Singh
Привет, Можете ли вы поделиться примером C ++ / Wrl, который вы упомянули в своем ответе? Sahil Singh
@SahilSingh Я не слежу за вашим последним комментарием, поэтому я попытался использовать классы и API, которые вы упомянули, и добавил еще один пример. Я перепутал с интенсивным использованием intellisense, и это работает. Кстати, все интерфейсы среды выполнения Windows наследуются от интерфейса IInspectable. Elliot

Похожие вопросы