Any ideas?
Also, I thought you had to "join" the thread to actually run it yet oddly enough they seem to run before join (they print before it prints "Started.") and I also thought join meant "pause main, run task until completion and then continue here." which would also imply they should be after started, and in order.
[edit]
It should be noted I'm using GCC 4.9.2 on Linux which may have faulty support for std::threads. I learned that the damn hard way after their regex was completely broken while I tried to learn it.
I am making a wild guess here but it may be because the first std::thread is created on the stack and has to be copied but the second thread is a temporary object. It may be using move semantics in the push_back function when there is an rvalue instead of an lvalue.
Please see my note, I posted before I fixed it. The first one^H^H^HLINE doesn't compile with a huge template error about allocater.
And per my thoughts, this SO post confirms what join does. But I'll look into your comments.
I'm not used to people replying so fast!
You don't have to join a thread to run, but you do have to start it (in this case perhaps automatically). If the threads have already finished though, join won't have any effect.
Yeah, it's what Edgar said. The copy constructor of std::thread is deleted (as it makes no sense to copy a thread, I suppose), so you need to move the variable using std::move. I.e. you'd do threads.push_back(std::move(t1));.
I suppose it's helpful that c++11 does that move stuff, but I'd still avoid storing a static std::thread in a container. I generally store non trivial objects in std containers as pointers.
The idiomatic pointer type for this purpose would be std::unique_ptr which also needs to be moved . These days I basically never use pointers unless I'm looking for some dynamic polymorphism.
Why not use Allegro's threads in stead?
https://www.allegro.cc/manual/5/ALLEGRO_THREAD
The idiomatic pointer type for this purpose would be std::unique_ptr which also needs to be moved . These days I basically never use pointers unless I'm looking for some dynamic polymorphism.
Eh. I also tend not to mess with std::unique_ptr much either.
It might also be useful to note that moving a variable will destroy the original and should not be used afterwards, much like in Rust, except Rust enforces it and C++ should but doesn't.
Afaik, all std::threads must end before they go out of scope, so you should call thread::join before they get destroyed.
for(int i = 0; i < 5; ++i) { std::thread t1(task1, "Hello"); threads.push_back(t1); // Tries to invoke the deleted copy constructor. threads.push_back(std::thread(task1, "Hello")); // Returns a rvalue. // Gets moved implicitly, you might want to use emplace_back to construct the thread directly in the vector and avoid any unnecessary copies. std::thread t2(task1, "Hello"); threads.push_back(std::move(t2)); // Explicitly move t2 // t2 should be considered 'consumed' and no longer be used. }
Took me a little while to figure these new move semantics out, but once you understand them they'll seem very elegant, at least that was my experience.
It might also be useful to note that moving a variable will destroy the original and should not be used afterwards
IIRC, moving a variable puts the original into a cleared/destructible state. The object is destructed at the end of its scope as normal, so it is still valid after a move. What its actual state is, though, is left undefined (you may or may not be able to do anything with it except let it destruct).
I realize my use of destroy might have been a little unclear, especially when I've used it with 2 different implied meanings.
But what I meant with the first instance of destroy is that it effectively can end up destroying the contents of the original and leaves it in an unspecified state, not that it calls the destructor, so you aren't supposed to use it in that state. Apparently an object you move from is called an xvalue (expiring value) now.
I guess I understand std::move now. std::forward is confusing.
But geez. Everything keeps getting more and more complex. I mean... std::namespace names for actual language semantics now?
I mean, what's next, std::addition? I was going to joke and say std::cast, std::reference, std::pointer or std::const, but those actually exist with slight variations in spelling.
Which means you have to use the std::namespace to use the most basic functionality of C++. I guess that's not automatically a "bad" thing if you're "all in" for C++, but it calls into question the philosophy of "only pay for what you use" if you're including more and more complex parts of the STL just to run a program.
Nothing stops you from just doing what std::move does manually:
threads.push_back((std::thread&&)t1);
Okay, I think I finally get what join means.
A std::thread will start execution the moment you create it. However, if you want to pause the calling thread until a child thread is finished, you would call join to ensure execution of the parent doesn't continue until after the child finishes.
That is, if I have asset_manager.load_data() dispatching a bunch of worker tasks representing the loading of game assets, I certainly don't want to have asset_manager.load_data() returning or making decisions until that data has finished loading.
So when I'm playing around with tiny functions, they're very likely to finish before main() even makes a decision about them.
Apologies if you already know this, but you can put
using namespace std;
just under your #includes to bring everything from std:: into scope (so you can put vector instead of std::vector) (link) - but bear in mind your definitions might clash with something from std::. You can also do it on a name-by-name basis, e.g
using std::vector; using std::thread;
And apologies again for thread hijack, but when iterating
for(auto& t : threads) { t.join(); }
I'm sure I saw somewhere it should be auto&& but I don't know why or what this means. Any ideas?
Pete
auto detects the type from the rhs, and && tells it to be an rvalue reference. Which can let you take a reference of a rvalue, or temporary, it's used in implementing move semantics and such.
You don't want an auto&& in a for loop unless you're planning on moving out those elements.
No really, what kind of an insane, cryptic mess is this all?
I really can't see how the C++ way with all those problems is easier than just a C array with malloc, realloc, and pointers to Allegro threads...
The only reason C++ needs this nonsense is because it has overloadable assignment operator. For C, you could say it moves by default!
Yeah, I see, now that you mention it... That's why most languages that allow operator overloading still don' t allow the assignment operator to be overloaded. Ruby comes to mind as such a language...
And yes, C assignment copies the data by default, which is the correct thing to do for a low level language.
Every time I think I'm getting a grasp on modern C++, it opens up a new steaming pile of complexity.
I've still not looked deep into move semantics. So far haven't needed it
You don't want an auto&& in a for loop unless you're planning on moving out those elements.
I think maybe it moves only if you give it an rvalue, otherwise it collapses `&&` to `&` and just takes a reference. Have a look at
http://en.cppreference.com/w/cpp/language/range-for
and
http://stackoverflow.com/questions/9162808/what-does-auto-do
No really, what kind of an insane, cryptic mess is this all?
Indeed.
If you look into `<memory>` or any of the standard headers it's all very complicated stuff - unfortunately for C++ (IMO) you do need to understand a little of how it works to use it efficiently - you can't just leave it all to the library writers.
I've still not looked deep into move semantics. So far haven't needed it
I mostly use it for things like
MyClass inst; //... do something with inst ... myvector.push_back(std::move(inst));
without the move it'll make a copy of inst to put into the vector, then inst itself will be destroyed some time later. If inst is big/complicated that could be expensive.
I personally don't use auto&& outside of template or generic lambdas.
without the move it'll make a copy of inst to put into the vector, then inst itself will be destroyed some time later. If inst is big/complicated that could be expensive.
Ok, makes sense. Mind you I typically use pointers for big complex objects. So it's kindof a moot point.
I personally don't use auto&& outside of template or generic lambdas.
I tend to only use auto (forget about &&) when I want to avoid typing out a long string of line noise that they call container iterator types. In that case you don't even want to make it a reference.
Now I have used T&& a fair bit in templates. or "Args&&... args" but thinking about it, that's probably best to trim down to "Args&... args" or drop the reference all together possibly. I'll have to test it. If the compiler isn't collapsing && down to & there, it'll case moves in places it won't be wanted in that code, sooo bad idea
append: Basically I stole some code from here and modified it a bit.