Okay @Ali , let's take a brief overview about some things (terms).
namespace std
- is a simple namespace, like:
namespace my_namespace {
int my_integer;
};
which contains tons of useful classes for programmers, written by ultra overpowered ones, and , as you understand it should be as flexible as possible (later "Statement 1"), casue different people have different needs.
And of course it obeys the general rules of c++ standart, as it contents also do.
Now lets talk about your desired std::thread
.
its a simple class that represents a single thread of execution. It allows you to execue your functions in "outerspace", and keeps in touch with our abstract "outerspace", it also handles some data:
- _id - thread ID
- _handle - the secret variable, which is the key for communication with "outerspace"
- and of course it keeps some data
about state of execution (does any entity executes now somewhere in
the "outerspace" where the key leads us to)
if you look up some references in more details and keep in mind our "Statement 1", you will notice the following information:
- "
std::thread
objects may also be in the state that does not represent any thread"
- "No two
std::thread
objects may represent the same thread of execution; std::thread
is not CopyConstructible or CopyAssignable, although it is MoveConstructible and MoveAssignable."
now you should come to a conclusion that std::thread
variable and executing entity are separate, but trying to remove std::thread
variable that is attached to an executing entity throws an exception.
but when the entity finishes execution, std::thread
variable keeps alive, and may be attached to any other entity.
for theese needs there are following methods:
join() // waits for a thread to finish its execution
// if it is attached to something, the code execution will not go futher until our entity finishes its execution.
detach() // permits the thread to execute independently from the thread handle
// detaches our std:: thread variable from executing entity, now our entity lives its own life. std::thread variable may be removed while the entity keeps alive
joinable() // checks whether the thread is joinable, i.e. potentially running in parallel context
// if thread is attached to something which is executing now, it returns true, otherwise false
Here some code examples that will clear out your misunderstanding:
#include <iostream>
#include <thread>
#include <chrono>
using namespace::std;
void some_func1() {
cout << "some_func1 thread started " << endl;
this_thread::sleep_for(chrono::seconds(2));
cout << "some_func1 thread finished " << endl;
}
void some_func2() {
cout << "some_func2 thread started " << endl;
this_thread::sleep_for(chrono::seconds(2));
cout << "some_func2 thread finished " << endl;
}
int main() {
thread some_thread;
cout << "Is some_thread joinable: " << some_thread.joinable() << endl;
some_thread = thread(some_func1);
cout << "Is some_thread joinable: " << some_thread.joinable() << endl;
some_thread.detach();
cout << "Is some_thread joinable: " << some_thread.joinable() << endl;
this_thread::sleep_for(chrono::seconds(1));
some_thread = thread(some_func2);
cout << "Is some_thread joinable: " << some_thread.joinable() << endl;
some_thread.join();
cout << "Is some_thread joinable: " << some_thread.joinable() << endl;
}
// Output is:
// Is some_thread joinable: 0
// some_func1 thread started
// Is some_thread joinable: 1
// Is some_thread joinable: 0
// some_func2 thread started
// Is some_thread joinable: 1
// some_func1 thread finished
// some_func2 thread finished
// Is some_thread joinable: 0
// Press any key to continue . . .
If you want to be ensured, that your entity finishes execution before your thread variable removes you may wrap it in another class and call join()
in destructor, as you mentioned in question.
or you may also wrap it in another one which will call detach()
in destructor.
theese 2 ways will prevent you from huge amount of crushes.
#include <iostream>
#include <thread>
#include <chrono>
using namespace std;
typedef void Myfunc();
void some_func1() {
cout << "some_func1 thread started " << endl;
this_thread::sleep_for(chrono::seconds(2));
cout << "some_func1 thread finished " << endl;
}
void some_func2() {
cout << "some_func2 thread started " << endl;
this_thread::sleep_for(chrono::seconds(2));
cout << "some_func2 thread finished " << endl;
}
class ICareAboutThread {
std::thread thread_;
public:
ICareAboutThread( Myfunc f = nullptr) : thread_(f) {};
~ICareAboutThread() { join(); }
bool joinable() { return thread_.joinable();}
void join() { thread_.join();}
void detach() { thread_.detach();}
// other constructors : move , safe copying - if necessary;
};
class IDontCareAboutThread {
std::thread thread_;
public:
IDontCareAboutThread(Myfunc f = nullptr) : thread_(f) {};
~IDontCareAboutThread() { detach(); }
bool joinable() { return thread_.joinable(); }
void join() { thread_.join(); }
void detach() { thread_.detach(); }
// other constructors : move , safe copying - if necessary;
};
int main() {
ICareAboutThread i_care(some_func1);
this_thread::sleep_for(chrono::seconds(1));
IDontCareAboutThread i_dont_care(some_func2);
return 0;
}
// Output is:
// some_func1 thread started
// some_func2 thread started
// some_func1 thread finished
// Press any key to continue . . .
now i hope its crystal clear for you, if you understand when variable removes :D