Related concepts of multi-threading in C++ and the use of multi-threading classes

1. Multi-threading related concepts

1.1 Concurrency, parallelism, seriality

Concurrency (Concurrent): Concurrency refers to two ormore events running in the same time interval. In the operating system, it means that several programs are in a period of time between starting and running, and these programs are all running on the same processor.
Only one instruction can be executed at the same time, but multiple process instructions are executed in rapid rotation, which gives the macro effect of multiple processes executing at the same time. , but in a microscopic sense, they are not executed at the same time. They just divide the time into several segments so that multiple processes can execute quickly and alternately.

Parallel (Parallel): Parallel refers to two ormultiple events running at the same time . When the system has more than one CPU, when one CPU executes a process, the other CPU can execute another process. The two processes do not seize each other's CPU resources and can proceed at the same time. This method is called parallel (Parallel). In fact, the factor that determines parallelism is not the number of CPUs, but the number of CPU cores. For example, multiple cores of a CPU can be parallelized. As shown below. 当线程数超过cpu核心数时,部分线程变成了并发执行。
Insert image description here
Serial: Parallel and serial refer to how tasks are performed. Parallelism means that multiple tasks can be executed simultaneously. Serial means that when there are multiple tasks, each task is executed in order. Only after one is completed can the next one be carried out. It is impossible for them to overlap in time. As shown below:
Insert image description here

1.2 Synchronous and asynchronous

Blocking(blocking), Non-blocking (non-blocking): can be simply understood as required Can you get a response immediately when you do something? If you cannot get a return immediately and need to wait, then it is blocked (the process or thread is blocked there and cannot do other things), otherwise it can be understood as non-blocking (waiting) You can do other things in the process).

Synchronization(synchronous): When a process executes a request, if the request takes a while to return information, then the process will wait forever. The execution continues until the return information is received;
同步是阻塞模式,其相当于单线程中的串行模式
Synchronization is equivalent to when the client sends a request to the server, and while waiting for the server to respond to the request, the client does nothing else. When the server is finished, it returns to the client. In this case, the client needs to wait forever.

Asynchronous(asynchronous): means that the process does not need to wait forever, but continues to perform the following operations regardless of the status of other processes. When a message returns, the system will notify the process to process it, which can improve execution efficiency.
异步是非阻塞模式,多线程都是异步的
Asynchronous is equivalent to when the client sends a request to the server. While waiting for the server to respond, the client can do other things, which saves time and improves efficiency

1.3 Process and thread

Process (Process): It is a running program entity, and includes this running program All system resources occupied in it, such as CPU (registers), IO, memory, network resources, etc. If the same program is run twice at the same time, they are two independent processes.

Thread (Thread): It is the smallest unit that the operating system can perform calculation scheduling. It is included in the process and is the actual operating unit in the process. A thread refers to a single sequential control flow in a process. Multiple threads can run concurrently in a process, and each thread performs different tasks in parallel.

The difference between processes and threads:

  • Thread is the smallest unit of execution of program, and Process is the smallest unit for operating system to allocate resources; process is the unit for system resource allocation, and threads are scheduled by the system. unit.
  • A process consists of one or more threads. Threads are different execution routes of code in a process;
  • Processes are independent of each other, and processes cannot share resources, but threads within a process can share resourcesThe address space and other resources of the process where it is located. At the same time, the thread also has its own stack, stack pointer, program counter and other registers.
  • Scheduling and switching speed: Thread context switching (其包含共享资源) is much faster than process context switching (其不包含共享资源).

1.4 Multithreading

Multi-threading means that the program contains multiple execution streams, that is, multiple different threads can be run simultaneously in one program to perform different tasks. Each thread has its own private registers (such as stack pointer, program counter), but the code area is shared, that is, different threads can execute the same method. Multi-threading technology allows a single program to create multiple threads of parallel execution to complete their respective tasks, thereby improving overall processing performance.

Multi-threading is a mechanism that allows multiple instruction streams to be executed concurrently in a program. Each instruction stream is called a thread and is independent of each other. Threads are similar to processes, but are more lightweight and focused on performing a single task. Multithreading is a special form of multitasking that can improve the efficiency of a system, especially when multiple tasks need to be completed simultaneously.

The above content is referenced from: https://blog.csdn.net/wang121213145/article/details/123828346

2. Key knowledge of multi-thread programming

2.1 How to create a thread

1. General method of creating threads
Description

  • The main thread starts executing from main. Once the main thread returns from main(), the entire program (process) ends.
  • If the main thread ends and other sub-threads have not finished executing, they will generally be forcibly terminated by the operating system (unless the sub-threads are set to detach)
  • Usually the sub-thread we create starts running from a function. Once the function finishes running, it means that the thread has ended.
  • If you want to keep the child thread running, do not let the main thread finish running (unless the child thread is set to detach)

2. General method of creating sub-threads

  • Include header files: #include <thread>
  • Write a function that the child thread starts executing (initial function)
  • Use thread() to create a child thread
  • Set the relationship between the main thread and sub-threads, join() or detach()
  • join() means that the child thread will block the running of the main thread, and detach() runs alone
2.1.1 thread()

(1) Let’s look at an example first. Here, the sub-thread myprint and the main thread main run concurrently.

#include <iostream>
#include <thread>
#include<windows.h>
using namespace std;
    
//子线程的初始函数
void printThread(int theadId)
{
    
    
    cout << "我的线程开始执行:" <<theadId<< endl;
    Sleep(1000);
    cout << "我的线程执行完毕" << endl;
}
    
//主线程在从main开始执行,一旦主线程从main()返回,则整个程序结束 
int main()
{
    
    
	//创建子线程(这两行在main函数里)
	int theadId=100;
    thread thread1(printThread,theadId);	//创建了线程,执行起点是printThread,同时让子线程开始执行
    thread1.join();			//主线程阻塞在这里,等子线程执行完。如果不加join,主线程结束了,子线程还在运行会导致程序崩溃
    
    cout << "Hello World!\n";	
	return 0;
}

-----operation result--------


我的线程开始执行:100
我的线程执行完毕
Hello World!\n

(2) About thread

  • thread is a class

  • thread thread1(printThread, theadId); uses the constructor to create a thread object, and the incoming parameters are a function name and the parameters of the corresponding function
    This line of code creates a thread Object, the execution starting point is the printThread function entry; and the child thread is started.

  • C++11 functional has expanded function pointers. Callable objects include function pointers and function objects.

2.1.2 join()
  • join is a method in the thread class

  • Function: Block the main thread (其不会阻塞子线程), let the main thread wait for the sub-thread to complete execution, then the sub-thread and the main thread merge, and then continue execution

  • If you comment out the thread1.join(); above, you may see confusing output (在部分电脑上会直接引发报错,因为主线程线运行结束了) and an exception prompt will pop up
    ( 1) Because the main thread and the sub-thread execute alternately, the printing is chaotic
    (2) Because the sub-thread ends before the main thread ends, the sub-thread is forced to end by the operating system, so an exception is reported

  • Once the threadjoin is removed, it can no longerdetach (otherwise an exception will be reported). We will control the child process ourselves

  • If the main thread has finished executing, but the sub-threads have not, this program is unstable, so we should try to ensure that the main thread ends after all sub-threads have finished running.

2.1.3 detach()

In traditional multi-threaded programs, the main thread has to wait for all sub-threads to finish executing before exiting, but C++11 adds detach(), so you don’t need to do this anymore.

  • detach is a method in the thread class

  • Function: Separate the sub-thread from the main thread.The separated sub-thread has no association with the main thread. After the main thread ends, if the sub-thread If the thread has not ended, it will continue to run in the background. When the child thread completes execution, the runtime library is responsible for cleaning up the thread-related resources

  • Once the thread is detached, it cannot be joined back (otherwise an exception will be reported), and we lose control of the process.

thread thread1(printThread);
thread1.detach();
2.1.4 joinable()
  • joinable is a method in the thread class

  • Function: Used to determine whether the thread can successfully use join and detch
    Return value:

      True:可以进行join()或detach();
    
      False:不能进行join()或detach()
    
  • When performing join() or detach() operations, make a judgment first and then operate

thread thread1(printThread);
if (thread1.joinable())
{
    
    
   thread1.join();
}

This can avoid system errors

The above content is referenced from: https://blog.csdn.net/wxc971231/article/details/105979443

2.2 Variable synchronization between threads

Inter-thread variable synchronization between C++ threads is realized based on shared memory (即多个线程使用同一个变量名). When thread a accesses the variable, thread b may be modifying the variable, so it needs to be set Thread safety mechanism.

1. Mutex lock (Mutex): Mutex lock is a synchronization mechanism used to prevent multiple threads from accessing shared resources at the same time. 主要方法包括两个,分别是 Lock 和 Unlock. When a thread acquires a mutex lock, other threads are blocked until the thread releases the lock. The advantage of a mutex is that it can avoid deadlock, but the disadvantage is that it may cause thread starvation.

2. Condition variable (Ondition variable): Condition variable is a synchronization mechanism used to pass signals between multiple threads. When a thread is waiting for a certain condition, it can call the wait() method of the condition variable to block itself. while (workq == NULL), 其workq为条件变量,通过while 死循环阻塞程序向后运行, multiple threads can be blocked or activated at one time through condition variables

3. Semaphore: Semaphore is a synchronization mechanism used to control access to shared resources. When a thread needs to access a shared resource, it must first obtain the semaphore. If the semaphore's value is 0, the thread will be blocked until another thread releases the semaphore. 其本质是通过一个变量来控制多个线程的运行,当变量值为n时,表示允许运行n个线程,每运行一个线程值减1;当n为0时,表示没有资源可供线程运行;当值为-n时,表示n个线程在等待运行;当线程运行结束,n的值则加一,表示释放一个线程执行机会, the advantage of semaphore is that it can avoid deadlock and thread starvation, but the disadvantage is that it may cause semaphore competition. 从实现上来说一个信号量可以是用mutex + counter + condition variable

4. Pipe (Pipe):Pipeline is an inter-process communication mechanism, but it can also be used for communication between threads. A pipe is a stream of bytes that can be used to pass data between two threads. The advantage of pipelines is that they are simple and easy to use, but the disadvantage is that they can only be used for communication between related threads. For details, please refer to: https://blog.csdn.net/skyroben/article/details/71513385

3. Use of multi-threaded classes based on object-oriented objects

A common problem in multi-threaded operations is: the producer-consumer problem. The producer is used to generate data, and the consumer is used to process data. The two interact with each other by sharing a variable.
Insert image description here
The thread creation method mentioned above is based on functions. In actual use, it is developed in an object-oriented manner, and function-related functions need to be encapsulated into classes. In object-oriented multi-threaded classes can also be implemented through std::thread. For details, please refer to the following code.

Its GetImage is the producer, DealImage is the consumer, imglist is the shared variable between the two, and mtx is the mutex lock.


#include <iostream> 
#include <thread>
#include <windows.h>
#include <opencv2/opencv.hpp>
using namespace std; 
using namespace cv;

using namespace std;
std::mutex mtx; // 保护对imglist的访问
vector<Mat> imglist;
//------------获取图像的线程类,imglist的元素在增多---------------
class GetImage
{
    
    
public:
	GetImage(vector<Mat>& imglist);
	void startWork();
	void work();
	static void threadFunc(GetImage*);
};

GetImage::GetImage(vector<Mat>& imglist) {
    
    
}

void GetImage::work()
{
    
    
	int count = 0;
	while (1)
	{
    
    
		Mat mat;
		mtx.lock();
		imglist.push_back(mat);
		mtx.unlock();
		cout <<"容器内数据量:  " <<imglist.size() << endl;
		Sleep(300);
	}
}

void GetImage::threadFunc(GetImage* arg)
{
    
    
	arg->work();
}

void GetImage::startWork()
{
    
    
	std::thread work_thread(threadFunc, this);
	work_thread.detach();
}

//------------处理图像的线程类,imglist的元素在减少---------------
class DealImage
{
    
    
public:
	DealImage(vector<Mat>& DealImage);
	void startWork();
	void work();
	static void threadFunc(DealImage*);
};

DealImage::DealImage(vector<Mat>& imglist) {
    
    
}
void DealImage::work()
{
    
    
	int count = 0;
	while (1)
	{
    
    
		if (imglist.size() > 0) {
    
    
			Mat mat = imglist[imglist.size()-1];//获取最后一个元素
			cout << "获取到一张图片 ,剩余图像:" << imglist.size() - 1 <<endl;

			mtx.lock();
			imglist.pop_back();//删除最后一个元素
			mtx.unlock();
			//---这里写图像处理函数---
		}
		else {
    
    
			cout << "--------没有获取到一张图片--------" << endl;
		}
		Sleep(400);
	}
}

void DealImage::threadFunc(DealImage* arg)
{
    
    
	arg->work();
}

void DealImage::startWork()
{
    
    
	std::thread work_thread(threadFunc, this);
	work_thread.detach();
}


int main()
{
    
    
	GetImage* t1 = new GetImage(imglist);
	t1->startWork();
	DealImage* t2 = new DealImage(imglist);
	t2->startWork();


	while (1)
	{
    
    
		Sleep(1);
	}

	return 0;
}

Guess you like

Origin blog.csdn.net/m0_74259636/article/details/135010065