0

Is there any potential problem in the code snippet below if std::future::get() would not be called?

I did several tests. It seems that the said code works well without invoking std::future::get() even if it takes std::async a long time to finish its work . It's really out of my expectation.

    #include<future>
    #include<iostream>
    #include<array>
    #include<algorithm>
    #include<thread>
    #include<vector>

    std::array<int, 100000> arr;
    int sum=0;

    struct Wrapper
    {   
        void consume()
        {
            std::cout << "consumer:" << std::this_thread::get_id() << std::endl;
            std::for_each(arr.begin(), arr.end(), [](int val) {sum+=val; });
        }

        void produce()
        {
            std::cout << "producer:" <<std::this_thread::get_id() << std::endl;
            
            int a=0;
            while(true)
            {
                if(a++>1e9)
                {
                    break;
                }
            }
        }
    };


    int main()
    {
        std::fill(arr.begin(), arr.end(), 1);

        std::cout << "main:" <<std::this_thread::get_id() << std::endl;

        Wrapper wrap;

        std::vector<std::future<void>> vec;
        vec.push_back(std::async(std::launch::async, &Wrapper::produce, &wrap));
        vec.push_back(std::async(std::launch::async, &Wrapper::consume, &wrap));

        #ifdef WAIT  //Is there any potencial problem if the block below does not run?
        for(auto& future:vec)
        {
            future.get();
        }
        #endif
    }
John
  • 2,963
  • 11
  • 33
  • related/dupe: https://stackoverflow.com/questions/63952768/do-you-need-to-store-the-stdfuture-return-value-from-stdasync – NathanOliver Jun 08 '22 at 01:48
  • @NathanOliver I carefully read the post. `{std::async(std::launch::async, []{std::this_thread::sleep_for(std::chrono::milliseconds(1000)); });}` creates a temporary object of `std::future` and the destructor of the said temporary object waits for the end of the task? Am I right? – John Jun 08 '22 at 02:55
  • @NathanOliver What's more. But the rule (i.e. `~future()` may block if this was the last reference to the shared state. ) seems only guaranteed by `C++14` and afterwards other than `C++11`. But my code snippet seems really work with `C++11`. – John Jun 08 '22 at 03:09

1 Answers1

0

As per the document about std::future destructor (emphasis mine):

these actions will not block for the shared state to become ready, except that it may block if all of the following are true: the shared state was created by a call to std::async, the shared state is not yet ready, and this was the last reference to the shared state. (since C++14)

But it seems only guaranteed by C++14 and afterwards other than C++11.

And for some cases, std::future::get() still needs to be explicitly called, for example:

    #include<future>
    #include<iostream>
    #include<array>
    #include<algorithm>
    #include<thread>
    #include<vector>

    std::array<int, 100000> arr;
    int sum=0;

    struct Wrapper
    {   
        void consume()
        {
            std::cout << "consumer:" << std::this_thread::get_id() << std::endl;
            std::for_each(arr.begin(), arr.end(), [](int val) {sum+=val; });
        }

        void produce()
        {
            std::cout << "producer:" <<std::this_thread::get_id() << std::endl;
            
            int a=0;
            while(true)
            {
                if(a++>1e9)
                {
                    break;
                }
            }
        }
    };


    int main()
    {
        std::fill(arr.begin(), arr.end(), 1);

        std::cout << "main:" <<std::this_thread::get_id() << std::endl;

        Wrapper wrap;

        std::vector<std::future<void>> vec;
        vec.push_back(std::async(std::launch::async, &Wrapper::produce, &wrap));
        vec.push_back(std::async(std::launch::async, &Wrapper::consume, &wrap));

        std::cout << sum << std::endl;
        #if 1
        for(auto& future:vec)
        {
            future.get();
        }
        #endif
        std::cout << sum << std::endl;
    }

Here is the output of the said code snippet above:

main:140198225385280
0
producer:140198225381120
consumer:140198216988416
100000
John
  • 2,963
  • 11
  • 33