locked
Task continuation

    Question

  • Hi , consider the below code:

    task<DeviceInformationCollection^>(DeviceInformation::FindAllAsync(Selector, nullptr))
            .then([this](DeviceInformationCollection^ interfaces)
        {
            for_each(begin(interfaces), end(interfaces),[this](DeviceInformation^ deviceInterface)
            {

                 DoSomeOperation(deviceInterface);

            });
     }).then([this]()
      {
             DoAnotherOperation();
         });

    As per task-then construct, can we ensure that DoAnotherOperation() will be called only after DoSomeOperation() has been called for all interfaces in the DeviceInformation Collection?  I want DoAnotherOperation() to be called only after the inner for loop has been completely executed. Sometimes it works as expected and sometimes not. In my case, the function DoSomeOperation() also has a task -then statement inside it..

    Thursday, June 21, 2012 10:57 AM

All replies

  • Could you provide an example of your implementation of DoSomeOperation()? Specifically the use of tasks within that method.

    Thanks


    David Lamb

    Friday, June 22, 2012 12:40 AM
    Moderator
  • Can you be more specific about what you mean when you say that you want "DoAnotherOperation() to be called only after the inner for loop has been completely executed"?  The way this is written right now, the continuation calling DoAnotherOperation will indeed only happen after the loop completes.  Since I see a comment saying that "DoSomeOperation() also has a task -then statement inside it", is the behavior you are really looking for one in which DoAnotherOperation only executes after all of the DoSomeOperation calls complete, the asynchronous operations scheduled in them complete, and the continuations scheduled off them complete?

    Right now, what will happen is that the for_each loop will run, any asynchronous operations called inside the DoSomeOperation calls will be started, and then the loop and the continuation which contains it will finish.  The asynchronous operations in each DoSomeOperation, however, may not be finished by this point in time (and they may all run in parallel).  There will be no deterministic order between those continuations and the one calling DoAnotherOperation.

    If you are looking for all of those operations to complete, the continuation with DoAnotherOperation must be scheduled from some task which will complete only after all of the asynchronous operations in the DoSomeOperation calls complete.  The continuation with the loop can encapsulate such a result in a task and return the task.  There are a variety of ways one might do that (and they differ depending on whether the starting of operations in each DoSomeOperation should be serialized or not).  The simplistic approach in pseudo-code (and I present it only for clarity of example) is for the loop task to have something like this:

    for_each(…)
    {
        task = task.then(
            [=](T val)
            {
                return DoSomeOperation(deviceInterface);
            });
    }
    
    return task;
    For more elegant ways of doing this (e.g.: that don't precreate all of the tasks and offer more flexibility), you might want to browse this blog entry or some of the Windows 8 samples which have loops over asynchronous operations (e.g.: the StreamSocket C++ sample).
    Friday, June 22, 2012 1:17 AM
  • If you are looking for all of those operations to complete, the continuation with DoAnotherOperation must be scheduled from some task which will complete only after all of the asynchronous operations in the DoSomeOperation calls complete.

      In my case there is no way we can know when the async operations in  DoSomeOperation()will finish so that I can schedule another task to start the second function DoAnotherOperation() . I referred to the samples but I didnt find any code similar to my problem. Let me give you code sample as below. Consider the tasks and functions only.

    void Test::MainPage::Button_Click_1(Platform::Object^ sender, Windows::UI::Xaml::RoutedEventArgs^ e)
    {
     auto selector = "Some Selector";
     task<DeviceInformationCollection^>(DeviceInformation::FindAllAsync(selector, nullptr))
                .then([this](DeviceInformationCollection^ interfaces)
            {
       
       std::for_each(begin(interfaces), end(interfaces),
                    [this](DeviceInformation^ deviceInterface)
                {
                      DoSomeOperation( deviceInterface );

                });
       
            }).then([this]()
       {

              DoAnotherOperation();
        
       });
    }

     void Test::MainPage::DoSomeOperation(DeviceInformation^ deviceInterface)
     {
      auto Properties = ref new Vector<String^>();
      Properties->Append( "Some Property" );
      task<DeviceInformation^>(DeviceInformation::CreateFromIdAsync( deviceInterface->Id, Properties )).
       then([this](DeviceInformation^ DevInfo)
      {
      
       std::for_each(begin( DevInfo->Properties ), end( DevInfo->Properties ), [this]( Windows::Foundation::Collections::IKeyValuePair<String^,Object^>^ Prop )
       {
        String^ Key = Prop->Key;
        if( Prop->Key == "Some Property" )
        {
         String^ ContinerID = "Some ID";
         auto Prpt = ref new Vector<String^>();
         Prpt->Append( "Some Other Property" );
         task<PnpObject^>(PnpObject::CreateFromIdAsync( PnpObjectType::DeviceContainer , ContinerID, Prpt ))
        .then([this, ContinerID ]( PnpObject^ PnpDev )
         {
          DoThisOperation( PnpDev, ContinerID );
         
         });

        }
       });
      });
     }

     void Test::MainPage::DoThisOperation( PnpObject^ PnpDev, String^ ContinerID )
     {
      IMapView<String^, Object^>^ MyMap = PnpDev->Properties;
      for_each( begin( MyMap), end( MyMap ), [this]( IKeyValuePair<String^, Object^>^ prop )
      {
          if( prop->Key == "Some Other Property" )
          {
            // Display the value of property.
          }
      });
     }

    ie. I am calling DoSomeOperation() from the continuation of a task. In DoSomeOperation(), again I am creating a task, from this task I am creating another task. From this final task, I am calling DoThisOperation() . DoThisOperation() simply prints some value and there is tasks in this. ie. DoSomeOperation() actually finds some devices and displays its details.All these functions and tasks are spawned from DoSomeOperation(). I want DoAnotherOperation() to start only after DoSomeOperation()( and of course, the tasks and functions it spawns ) have been completed. Any inputs?



    Tuesday, June 26, 2012 6:45 PM
  • Hi,

    You can know when the async operations in DoSomeOperation() finish 
    just declare DoSomeOperation() be async function
    And then, just follow task.then syntax 
    ex : DoSomeOperation().then{[](){ /*....*/  }};

    In the otherhand, you can using when_all to wait for_each task list complete
    Follow this code.
    files is a IVectorView<StorageFile^>^
    WriteFilesAsync is async function

    The when_all will do something when all the WriteFilesAsync() completed

    std::vector<task<bool>> FileTasks;
    std::for_each(begin(files), end(files), [&FileTasks](StorageFile ^file)
    {
    	FileTasks.push_back( WriteFilesAsync(file) );
    });  
    
    when_all(begin(FileTasks), end(FileTasks))
    	.then([this](std::vector<bool> bResult)
    { /* Do something */  });

    Wednesday, June 27, 2012 10:24 AM