Reputation: 23
I am a beginner at c++ and was messing around with dynamic memory and asynchronous functions and I was not sure why the following code did not work.
What I wanted to do is to basically dynamically create an object of Loop
and an async function which calls the start
method of the object, each assigning to a different map variable with the same id as key for both (randomthing
in the example code).
Expected: print out numbers 10 to 1 every second.
Result: Segmentation Fault
#include <iostream>
#include <vector>
#include <map>
#include <chrono>
#include <future>
class Loop{
public:
Loop(int t_):t(t_){}
void start(){
while(t>0){
std::cout << t << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
t--;
}
}
private:
int t;
};
int main()
{
std::map<std::string, std::future<void>> store;
std::map<std::string, Loop*> storeClass;
storeClass["randomthing"] = new Loop(10);
store["randomthing"] = std::async(std::launch::async, [&storeClass](){
std::cout << "starting" << std::endl;
storeClass["randomthing"]->start();
std::cout << "finished" << std::endl;
delete storeClass["randomthing"];
});
return 0;
}
I messed around it a bit more and found that this modification works:
Declaring store as std::future<void> store
instead of a map and directly assigning the async function to it after creating the Loop object.
Why the example code does not work and why can the modification fix the issue?
I am compiling the program with -std=c++11.
Upvotes: 0
Views: 812
Reputation: 140960
You have to make sure that all variables are valid within the lifetime when they are used. Your code:
std::async(std::launch::async, [&storeClass](){
storeClass["randomthing"]->start(); // uses storeClass
});
return 0;
} // calls storeClass destructor
Has a race condition, where storeClass
destructor and use of storeClass
are unsequenced. In result, when destructor happens to be called after async, then it's fine, otherwise you can get a seg fault.
Do not use raw pointers - use std::unique_ptr
for Loop*
.
To solve it you could make storeClass
a std::shared_ptr
or unique_ptr
and copy/move it into the lambda run in async
. Or just wait for the async to complete:
store["randomthing"] = std::async(std::launch::async, [&storeClass](){
.. use storeClass ..
});
store["randomthing"].wait(); // wait for calling `storeClass["randomthing"]
return 0;
} // will destroy storeClass
Upvotes: 1