Why is it impossible to move a variable to another thread

What is the reason that you cannot move an object to another thread? There are situations when it can be useful. For instance:

You create a loop that accepts incoming socket connections. It would be nice to move the incoming connections to another thread that will handle the connection. You no longer need a connection in the adoption loop. So why should you create a pointer?

A small test case:

#include <iostream> #include <thread> using namespace std; class Pointertest { public: Pointertest() {cout << "Constructor";} Pointertest(Pointertest &pointertest) {cout << "Copy";} Pointertest(Pointertest &&pointertest) {cout << "Move";} ~Pointertest() {cout << "Destruct";} }; void foo(Pointertest &&pointertest) { } int main() { Pointertest pointertest; foo(std::move(pointertest)); //Works thread test(foo,std::move(pointertest)); //**cannot convert parameter 1 from 'Pointertest' to 'Pointertest &&'** } 
+4
source share
2 answers

The std::thread constructor should handle the arguments that you give it a little differently than most forwarding functions.

The reason for this is related to questions about when the thread actually starts. If the part of the function call that actually created the function argument starts long after the thread object is created (which is completely legal behavior), then the object that needs to be transferred from may be destroyed long ago.

Just consider the modified version of your code:

 std::thread some_func() { Pointertest pointertest; thread test(foo,std::move(pointertest)); return test; } 

This is absolutely true (the thread will be removed from the function). However, there is a big problem. foo may not have been called yet. And since foo takes its parameter by reference, it now has a reference to the destroyed stack variable.

This is bad. But even if foo took its parameter by value, it will not change anything. Since the actual movement to this parameter does not occur until some indefinite time after the start of the flow. Trying to pass to a parameter will still use the rvalue reference for the destroyed stack variable. Which is bad again.

Consequently, the std::thread constructor does something else. It copies / moves the arguments you pass to internal memory (this is done in the current thread). He then uses these values ​​as arguments to actually call the function (this is done in a new thread).

According to the standard, a thread constructor should treat the transfer of these internal variables to your functions as temporary. The standard states the state INVOKE (DECAY_COPY ( std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) , where the DECAY_COPY material occurs in the original stream, and the INVOKE part in a new thread.

So, it seems that your thread implementation does not allow forwarding parameters that are not copied correctly. You should be able to pass in a type that cannot be copied; arguments must be MoveConstructible .

Thus, this will be a mistake in your implementation.

+15
source

It is possible. Fixing the signature of your copy constructor makes me work for me:

 class Pointertest { public: Pointertest() {cout << "Constructor";} Pointertest(Pointertest const& pointertest) {cout << "Copy";} // ^^^^^^ Pointertest(Pointertest &&pointertest) {cout << "Move";} ~Pointertest() {cout << "Destruct";} }; 

Also, be sure to join your thread (or disconnect from it) before the thread object goes beyond:

 int main() { Pointertest pointertest; thread test(foo, std::move(pointertest)); test.join(); // ^^^^^^^^^^^^ } 
+5
source

Source: https://habr.com/ru/post/1480547/


All Articles