1. Simple use of thread
#include <iostream> #include <thread> #include <Windows.h> using namespace std; void thread01() { for (int i = 0; i < 5; i++) { cout << "Thread 01 is working !" << endl; Sleep(100); } } void thread02() { for (int i = 0; i < 5; i++) { cout << "Thread 02 is working !" << endl; Sleep(200); } } int main() { thread task01(thread01); //Create a thread class object task01. Take thread01 as the parameter constructed when a thread is created. The created thread 01 executes the tasks in the thread01 function thread task02(thread02); task01.join(); //Add the 01 sub thread, block the main thread, and wait until 01 runs task02.join(); //Add the 02 sub thread, block the main thread, and wait until 02 runs for (int i = 0; i < 5; i++) { cout << "Main thread is working !" << endl; Sleep(200); } system("pause"); }
Sub threads 01 and 02 will not block each other. The main thread will continue when both sub threads end
2. Thread synchronization
#include <iostream> #include <thread> #include <Windows.h> using namespace std; int a = 0; void thread01() { for (int i = 0; i < 10000000; i++) a++; } int main() { thread task01(thread01);//Take thread01 as the parameter when a thread is created. The created thread 01 executes the task in thread01 function for (int i = 0; i < 10000000; i++) a++; task01.join();//Add the 01 sub thread, block the main thread, and wait until 01 runs cout << a << endl; system("pause"); }
When i is set to 10000000, there will be an error result. The time of a for loop is greater than the interval between two statements. You can see that the global variable i in the code increases 10000000 times in the main thread and 10000000 times in the main thread. The result should be 20000000, but the displayed result is 13010045, It is speculated that the main thread has performed 3010045 times. Before it is finished, the sub thread is added to block the main thread and then execute the sub thread. Therefore, the sub process is set as an empty function. It is expected that the final displayed result is 3010045
It can be seen that the result is not our guess, indicating that the addition of sub processes will not block the operation in the main thread
View our a + + disassembly subprocesses:
a++; 00D02643 mov eax,dword ptr [a (0D0E2D4h)] //Give the value mov with address 0D0E2D4h in memory to register eax 00D02648 add eax,1 //eaxadd1 00D0264B mov dword ptr [a (0D0E2D4h)],eax //Put the value of eax back into 0D0E2D4h 00D02650 jmp thread01+31h (0D02631h)
C + + is a high-level language, and a sentence self increasing operation becomes four sentences after being translated into assembly language
Check the disassembly of a + + in the main process:
00D03C39 mov eax,dword ptr [a (0D0E2D4h)] 00D03C3E add eax,1 00D03C41 mov dword ptr [a (0D0E2D4h)],eax 00D03C46 jmp std::thread::join+47h (0D03C27h)
You can see that the address of the register stored by a is the same, but the address of the instruction is different.
Concurrency is to alternately perform two or less operations, assuming that each operation executes for X milliseconds
When the child process is added, for example, only the first sentence is executed after executing the process for 1X milliseconds. Suppose a is 5000 at this time, then execute process 2, execute X milliseconds, a increases by 3000, and then switch back to process 1. Then execute the second game that has not been executed before, a changes back to 5000, and the 3000 increased in process 2 disappears.
This is the problem of synchronization
3. Method of thread synchronization
Atomic operation: it means that when a thread accesses resources, it ensures that other threads will not access the same resources (let the multiple lines of assembly code corresponding to one line of C + + code be regarded as a whole, either executed together or not executed at all)
for (int i = 0; i < 10000000; i++) { InterlockedAdd((long*)&a, 1); }
But this is limited to addition, and the generalization is not high
Therefore, the concept of lock is introduced
Create a critical area object before use, which is equivalent to buying a lock
CRITICAL_SECTION g_cs;
Initialize the lock InitializeCriticalSection first, delete the lock DeleteCriticalSection after use, lock the EnterCriticalSection before use, and unlock the LeaveCriticalSection after use;
#include <iostream> #include <thread> #include <Windows.h> using namespace std; int a = 0; //Create critical zone object -- equivalent to lock CRITICAL_SECTION g_cs; void thread01() { for (int i = 0; i < 10000000; i++) { //Lock when you come in EnterCriticalSection(&g_cs); a++; //Go out and unlock LeaveCriticalSection(&g_cs); } } int main() { InitializeCriticalSection(&g_cs); thread task01(thread01); for (int i = 0; i < 10000000; i++) { //Lock when you come in EnterCriticalSection(&g_cs); a++; //Go out and unlock LeaveCriticalSection(&g_cs); } task01.join(); cout << a << endl; system("pause"); //Delete the lock when not in use DeleteCriticalSection(&g_cs); }