locked
std::vector fails to iterate in concurrent::task

    Question

  • I have a std::vector that iterates fine when iterated over in the constructor of the owning class.  However, when I iterate over it in an async method it fails.  I don't understand how this can be the case, since, to my understanding, std::vector<> is thread agnostic.  It isn't thread safe, but as long as you serialize use, then it isn't going to have any issues.

    The following code is in a C++ Blank App (XAML) project.

    MainPage.xaml.cpp:

    //
    // MainPage.xaml.cpp
    // Implementation of the MainPage class.
    //
    
    #include "pch.h"
    #include "MainPage.xaml.h"
    #include "Test.h"
    #include <ppltasks.h>
    
    using namespace App2;
    
    using namespace Platform;
    using namespace Windows::Foundation;
    using namespace Windows::Foundation::Collections;
    using namespace Windows::UI::Xaml;
    using namespace Windows::UI::Xaml::Controls;
    using namespace Windows::UI::Xaml::Controls::Primitives;
    using namespace Windows::UI::Xaml::Data;
    using namespace Windows::UI::Xaml::Input;
    using namespace Windows::UI::Xaml::Media;
    using namespace Windows::UI::Xaml::Navigation;
    
    // The Blank Page item template is documented at http://go.microsoft.com/fwlink/?LinkId=234238
    
    MainPage::MainPage()
    {
    	InitializeComponent();
    }
    
    /// <summary>
    /// Invoked when this page is about to be displayed in a Frame.
    /// </summary>
    /// <param name="e">Event data that describes how this page was reached.  The Parameter
    /// property is typically used to configure the page.</param>
    void MainPage::OnNavigatedTo(NavigationEventArgs^ e)
    {
    	(void) e;	// Unused parameter
    
       X::Test t;
       concurrency::task<void> t2(t.IterateAsync());
       t2.then([](){
       });
    }

    Test.h

    #pragma once
    
    #include <vector>
    
    namespace X
    {
       public ref class Test sealed
       {
       public:
          Test();
          virtual ~Test();
          void Iterate();
          Windows::Foundation::IAsyncAction^ IterateAsync();
       private:
          std::vector<int> v;
       };
    }

    Test.cpp

    #include "pch.h"
    #include "Test.h"
    #include <ppltasks.h>
    
    using namespace X;
    using namespace concurrency;
    
    Test::Test()
    {
       Iterate();
    }
    
    Test::~Test()
    {
    }
    
    void Test::Iterate()
    {
       for (auto x : v)
       {
          int i = x;
       }
    }
    
    Windows::Foundation::IAsyncAction^ Test::IterateAsync()
    {
       return create_async([this]()->void
       {
          Iterate();
       });
    }


    • Edited by diltsman Friday, December 14, 2012 11:41 PM Naming files
    Friday, December 14, 2012 11:38 PM

Answers

  • Your program is racy:  you've declared 't' with automatic destruction semantics, so the X::Test object denoted by 't' will be disposed of (its destructor will be run) when OnNavigatedTo returns.

    However, the asynchronous operation that you start in IterateAsync may not run until after OnNavigatedTo returns.  If that happens, it will attempt to use the destroyed X::Test object.

    You can avoid this problem by not using automatic destruction semantics, by declaring 't' as:

        X::Test^ t(ref new X::Test());

    Here, the destructor for the X::Test object will not be run until all strong references to the object have been released.  Since the asynchronous operation holds a strong reference to the X::Test object, this ensures that the object is not destroyed until after the asynchronous operation completes.

    • Marked as answer by diltsman Sunday, December 16, 2012 6:13 PM
    Sunday, December 16, 2012 4:47 AM