C++ 다중 스레드 시나리오에서 변수의 조기 릴리스로 인해 스택 메모리 예외가 발생함

다중 스레드 시나리오에서 스택 메모리 예외

자식 스레드에서 현재 함수의 리소스를 사용하려고 시도하는 것은 매우 위험 하지만 C++에서는 이를 지원합니다. 따라서 C++에서 이 작업을 수행하면 스택 메모리 예외가 발생할 수 있습니다.

일반 코드

#include <iostream>
#include <thread>
#include <windows.h>

// 线程函数,用于执行具体的任务
void fun(int param)
{
    
    
	std::cout << __FUNCTION__ << " param:" << param << std::endl;
	Sleep(3000);
	param = 1;
	std::cout << __FUNCTION__ << " param:" << param << std::endl;
}

// 线程函数,用于执行具体的任务
void threadFunction() {
    
    
	
	// 在这里执行线程的具体任务
	int num = 2;
	//在函数中还启动了一个线程
	std::thread funThread([num]()
		{
    
    
			fun(num);
		});
	funThread.detach();
}

int main() {
    
    

	const int numThreads = 3;
	std::thread threads[numThreads];
	// 创建并启动多个线程
	for (int i = 0; i < numThreads; ++i) {
    
    
		threads[i] = std::thread(threadFunction);
	}
	//等待所有线程执行完毕
	for (int i = 0; i < numThreads; ++i) {
    
    
		threads[i].join();
	}
	Sleep(1000);
	std::cout << "All threads have completed." << std::endl;

	return 0;
}

위의 내용은 일반적인 멀티스레드 코드입니다.

예외 코드

그러나 멀티스레드 매개변수 전달이 참조 전달로 설정된 경우 아래와 같이 스택 메모리 예외가 발생할 수 있습니다.

#include <iostream>
#include <thread>
#include <windows.h>

// 线程函数,用于执行具体的任务
void fun(int& param)//传参修改为引用传递
{
    
    
	std::cout << __FUNCTION__ << " param:" << param << std::endl;
	Sleep(3000);
	param = 1;
	std::cout << __FUNCTION__ << " param:" << param << std::endl;
}

// 线程函数,用于执行具体的任务
void threadFunction() {
    
    
	
	// 在这里执行线程的具体任务
	int num = 2;
	//在函数中还启动了一个线程,传参为引用
	std::thread funThread([&num]()
		{
    
    
			fun(num);
		});
	funThread.detach();
}

int main() {
    
    

	const int numThreads = 3;
	std::thread threads[numThreads];
	// 创建并启动多个线程
	for (int i = 0; i < numThreads; ++i) {
    
    
		threads[i] = std::thread(threadFunction);
	}
	//等待所有线程执行完毕
	for (int i = 0; i < numThreads; ++i) {
    
    
		threads[i].join();
	}
	Sleep(1000);
	std::cout << "All threads have completed." << std::endl;

	return 0;
}

컴파일은 성공했지만 실행에는 실패했습니다.
실행 결과:
여기에 이미지 설명을 삽입하세요.
위의 std::thread funThread( &num {fun(num);}); 는 참조로 전달되고 void fun(int& param) 도 참조 매개변수를 사용합니다. 이때 메모리 예외가 발생할 수 있습니다.

들어오는 매개변수 num은 로컬 매개변수이기 때문에 fun에서 param을 수정하거나 읽을 때 num이 해제되었을 수 있는데, 이때 param을 수정하거나 읽을 때 메모리 오류가 발생하게 되는데, 실제로는 스택 메모리 예외이다. 스택 메모리는 말할 수 없는 문제를 일으킬 수 있기 때문에 이는 매우 위험합니다. 이 코드에는 작업 스레드가 하나만 있으므로 여기에 오류가 보고되므로 작업 기능에서 충돌이 발생합니다. 그러나 코드에서 매개변수 param을 읽고 수정할 수도 있어 다른 스레드에서 예외가 발생하므로 이 문제를 찾기가 어려운 경우가 많습니다.

코드 최적화

물론 참조로 전달하면 임시 변수 생성을 피할 수 있다는 장점이 있지만, 변수가 복잡한 객체인 경우에도 메모리 소모를 크게 줄일 수 있습니다. 참조를 사용할 수는 없지만 std::move를 사용하여 변수 소유권을 이전하고 임시 변수 복사를 줄일 수 있습니다.

#include <iostream>
#include <thread>
#include <windows.h>

// 线程函数,用于执行具体的任务
void fun(int&& param)//右值传参
{
    
    
	std::cout << __FUNCTION__ << " param:" << param << std::endl;
	Sleep(3000);
	param = 1;
	std::cout << __FUNCTION__ << " param:" << param << std::endl;
}

// 线程函数,用于执行具体的任务
void threadFunction() {
    
    
	
	// 在这里执行线程的具体任务
	int num = 2;
	//在函数中还启动了一个线程
	std::thread funThread(fun, std::move(num));//使用move传入右值
	funThread.detach();
}

int main() {
    
    

	const int numThreads = 3;
	std::thread threads[numThreads];
	// 创建并启动多个线程
	for (int i = 0; i < numThreads; ++i) {
    
    
		threads[i] = std::thread(threadFunction);
	}
	//等待所有线程执行完毕
	for (int i = 0; i < numThreads; ++i) {
    
    
		threads[i].join();
	}
	Sleep(1000);
	std::cout << "All threads have completed." << std::endl;

	return 0;
}

추천

출처blog.csdn.net/weixin_44477424/article/details/132284340