Start and end of C + + thread running (code demonstration)

One main thread:

int main()
{
	//When the program runs, a process is generated, and the main thread to which the process belongs starts to run automatically
	cout << " I love China" << endl;//In fact, the main thread is executing. When the main thread returns from the main() function, the whole process is completed.


	return 0;
}
  1. The main thread starts to execute from the main() function, so the thread we create also needs to run from a function (initial function). Once this function runs, it means that our thread runs end.
  2. The flag of whether the whole process has been executed is: whether the main thread has been executed. If the main thread has been executed, it means that the whole process has been executed. At this time, if other sub threads have not been executed, they will also be forcibly terminated.
  3. Therefore, in general, if you want to keep the running state of the child thread (the thread created with your own code), you should keep the main thread running all the time, and don't let the main thread finish running. (there are exceptions)

2, Child thread creation

  • Include header file thread
#include<thread>
  • Threads created by ourselves also need to run from a function (initial function)
#include<iostream>
#include<vector>
#include<map>
#include<string>
#include<thread>
using namespace std;

//Create your own thread and run it from a function (initial function)
void myprint()
{
	cout << " My thread started" << endl;
	cout << " My thread has finished executing" << endl;
}
int main()
{
	//When the program runs, a process is generated, and the main thread to which the process belongs starts to run automatically
	//cout << " I love China" << endl;// In fact, the main thread is executing. When the main thread returns from the main() function, the whole process is completed.

	return 0;
}
  • Start writing code in main()
void myprint()
{
	cout << " My thread started" << endl;
	cout << " My thread has finished executing" << endl;
}
int main()
{

	thread myobj(myprint);//thread is a class that creates a class object myobj
	myobj.join();

	cout << " I love China" << endl;

	return 0;
}

Results at this time:
From the result, it seems that the child thread runs first and then the main thread
It must be clear that there are two threads running, which is equivalent to two lines walking at the same time in the whole program. Even if one is blocked, the other can run.

For thread myobj(myprint);, This statement includes the following points:

  • thread is a class that creates a class object myobj, and myprint is a callable object (construct the myobj object as an argument)
  • A thread is created, and the starting point (entry) of thread execution is myPrint;
  • The myprint thread starts executing (that is, when this statement is run, the child thread starts executing)

For myobj.join();, This statement includes the following points:

  • join(): join, that is, block, block the main thread, let the main thread wait for the execution of the sub thread, then the sub thread and the sub thread join, and then the main thread goes down.
  • That is, the main thread blocks here and waits for myprint() to finish executing. When the sub thread finishes executing, the jion will finish executing, and the main thread will continue to go down.
  • Bottom line: block the main thread and wait for the child thread to finish executing

If you do not add the sentence "join", the program will report an error and the print result is chaotic.

3, detach()

In general, if you want to keep the running state of the child thread (the thread you created with your own code), you should keep the main thread running all the time, and don't let the main thread finish running.
When we create many sub threads and let the main thread wait for the sub threads to end one by one, this programming method is not good,
Therefore, detach occurs to separate threads, that is, the main thread does not merge with the sub thread and goes its own way;

  • After detach, the thread object (myobj) associated with the main thread will lose its association with the main thread. At this time, the sub thread will reside and run in the background. This sub thread is equivalent to being taken over by the C + + runtime library. After the sub thread is executed, the runtime library is responsible for cleaning up the thread related resources (daemon thread).
//Create your own thread and run it from a function (initial function)
void myprint()
{
	cout << " My thread started" << endl;
	cout << " My thread has finished executing 1" << endl;
	cout << " My thread has finished executing 2" << endl;
	cout << " My thread has finished executing 3" << endl;
	cout << " My thread has finished executing 4" << endl;
	cout << " My thread is finished 5" << endl;
	cout << " My thread is finished 6" << endl;
	cout << " My thread has finished executing 7" << endl;
	cout << " My thread has finished executing 8" << endl;
	cout << " My thread is finished 9" << endl;
	cout << " My thread has finished executing 10" << endl;
}
int main()
{

	
	thread myobj(myprint);
	//myobj.join();
	myobj.detach();

	cout << " I love China1" << endl;
	cout << " I love China2" << endl;
	cout << " I love China3" << endl;
	cout << " I love China4" << endl;
	cout << " I love China5" << endl;
	cout << " I love China6" << endl;
	cout << " I love China7" << endl;
	cout << " I love China8" << endl;
	cout << " I love China9" << endl;
	cout << " I love China10" << endl;

	return 0;
}


From the results, the main thread and sub thread go their own way, and the sub thread stays in the background to run. When the main thread ends, the results of the sub thread cannot be printed here.

  • detach makes myprint lose our control.
    Once detach is called, you can no longer join, otherwise an exception will be reported.

4, joinable()
joinable(): judge whether join() or detach() can be used successfully;
Return true (you can join() or detach())
Or false

	thread myobj(myprint);
	if (myobj.joinable())
	{
		cout << "true!" << endl;
	}
	else
	{
		cout << "false!" << endl;
	}

	myobj.detach();

	if (myobj.joinable())
	{
		cout << "true-1!" << endl;
	}
	else
	{
		cout << "false-1!" << endl;
	}
	


5, Other thread creation techniques
1 use class objects, and a problem example

class TA
{
public:
	void operator()()//Overload (), without parameters
	{
		cout << " My thread operator()Here we go" << endl;
		cout << " My thread operator()completion of enforcement" << endl;
	}

};
int main()
{
	
	TA ta;
	thread myobj(ta);//ta: callable object
	myobj.join();

	cout << " I love China1" << endl;
	
	return 0;
}

If you want to detach with a detach thread:

 class TA
{
public:
	int& m_i;
	TA(int& i) :m_i (i){}//Constructor
	
	void operator()()//Overload (), without parameters
	{
		/*cout << " My thread operator() started "< < endl;
		cout << " My thread operator() finished executing "< < endl;*/
		cout << "1m_i The value of is:" << m_i << endl;
		cout << "2m_i The value of is:" << m_i << endl;
		cout << "3m_i The value of is:" << m_i << endl;
		cout << "4m_i The value of is:" << m_i << endl;
		cout << "5m_i The value of is:" << m_i << endl;
		cout << "6m_i The value of is:" << m_i << endl;
	}

};
int main()
{
	int myi = 6;
	TA ta(myi);
	thread myobj(ta);//ta: callable object
	//myobj.join();
	myobj.detach();

	cout << " I love China1" << endl;
	
	return 0;
}
	

In this example, the detach is used for thread separation, and the reference used in the child thread is passed. When the main thread is executed, the value of myi will be destroyed, but the child thread may not end its execution at this time, and unpredictable results will occur.

If value passing is used, it is equivalent to a copy of the value, and the end of the main thread does not affect the child thread.
It should also be noted that:
Q: once detach () is called, when the main thread finishes executing, is this ta object still there?
A: No, but it doesn't matter. This object is actually copied to the child thread. After the main thread is executed, the copy still exists.
Therefore, as long as there is no reference or pointer in the TA object, there will be no problem.

class TA
{
public:
	int m_i;
	TA(int i) :m_i (i)
	{
		cout << "TA()Constructor execution for" << endl;
	
	}

	TA(const TA& ta) 
	{
		m_i = ta.m_i;
		cout << "TA()Copy constructor execution" << endl;
	}
	~TA()
	{
		cout << "TA()Destructor execution" << endl;
	}
	
	void operator()()//Overload (), without parameters
	{
		/*cout << " My thread operator() started "< < endl;
		cout << " My thread operator() finished executing "< < endl;*/
		cout << "1m_i The value of is:" << m_i << endl;
		cout << "2m_i The value of is:" << m_i << endl;
		cout << "3m_i The value of is:" << m_i << endl;
		cout << "4m_i The value of is:" << m_i << endl;
		cout << "5m_i The value of is:" << m_i << endl;
		cout << "6m_i The value of is:" << m_i << endl;
	}

};
int main()
{
	int myi = 6;
	TA ta(myi);
	thread myobj(ta);//ta: callable object
	myobj.join();
	//myobj.detach();

	cout << " I love China1" << endl;
	
	return 0;
}


It can be seen from the results that a copy has occurred.
The first print destruct is the destruct of the child thread, and the second is the destruct of the main thread.

2 use lambda expression

int main()
{
	//Thread entry
	auto mylambthread = [] {
		cout << " My thread started" << endl;
		cout << " My thread has finished executing 10" << endl;
	};
	thread myobj(mylambthread);
	myobj.join();

	cout << " I love China1" << endl;
	
	return 0;
}

Keywords: C++

Added by thinkmarsh on Wed, 13 Oct 2021 21:41:10 +0300